FreeGridv1.15.1English

컬럼 기준 행 합치기

원본 행을 불러온 뒤 기준 컬럼 중 하나를 숨기거나 빼면, 나머지 기준이 같은 행끼리 합계·평균·최소·최대·개수로 한 행이 됩니다.

실행 예제컬럼 기준 행 합치기

원본 행을 불러온 뒤 컬럼을 빼면 나머지 기준이 같은 행끼리 합계·평균으로 합칩니다.

이 데모의 코드
import { createGrid, type ColumnDef } from '@freegridjs/core';
import { makeRows } from '../data';
import { button, separator, text, tr, type DemoContext } from '../ui';

export default function mount(ctx: DemoContext) {
  const { lang } = ctx;
  const h = (en: string, ko: string) => tr(lang, { en, ko });
  const money = { type: 'number', format: '#,##0', width: 120 } as const;
  const columns: ColumnDef[] = [
    { field: 'id', header: 'ID', type: 'number', format: '0', width: 80 },
    { field: 'country', header: h('Country', '국가'), width: 140 },
    { field: 'name', header: h('Name', '이름'), width: 160 },
    { field: 'company', header: h('Company', '회사'), width: 130 },
    { field: 'email', header: h('Email', '이메일'), width: 220 },
    { field: 'q1', header: h('Q1', '1분기'), ...money, aggFunc: 'sum' },
    { field: 'q2', header: h('Q2', '2분기'), ...money, aggFunc: 'sum' },
    { field: 'total', header: h('Annual revenue', '연매출'), ...money, width: 130, aggFunc: 'avg' },
  ];
  const grid = createGrid(ctx.host, {
    columns,
    data: makeRows(300),
    locale: lang,
    rollup: { by: ['country', 'name', 'company'] },
    summary: { rows: [{ label: h('Total', '합계'), columns: { q1: 'sum', q2: 'sum', total: 'avg' } }] },
  });
  grid.on('columnVisible', e => ctx.log(`${e.colId} ${e.visible ? h('shown', '보임') : h('hidden', '숨김')} → ${grid.getDisplayedRowCount()} ${h('rows', '행')}`));

  const show = (ids: string[], on: boolean) => ids.forEach(id => grid.setColumnVisible(id, on));
  text(ctx, h('Every row shows at first. Hide Name and rows with the same country and company merge: quarters are summed, annual revenue averaged. The column menu of a number column picks the aggregate.',
    '처음에는 모든 행이 보입니다. 이름을 숨기면 국가·회사가 같은 행끼리 합쳐져 분기는 합계, 연매출은 평균이 됩니다. 숫자 컬럼의 메뉴에서 집계 방식을 고를 수 있습니다.'));
  separator(ctx);
  button(ctx, h('Hide Name', '이름 숨기기'), () => show(['name'], false));
  button(ctx, h('Hide Name and Company', '이름·회사 숨기기'), () => show(['name', 'company'], false));
  button(ctx, h('Show all', '모두 보이기'), () => show(['name', 'company'], true));
  separator(ctx);
  button(ctx, h('Annual revenue: max', '연매출: 최대'), () => grid.setColumnAggFunc('total', 'max'));
  button(ctx, h('Annual revenue: average', '연매출: 평균'), () => grid.setColumnAggFunc('total', 'avg'));
  return grid;
}

원본 행을 불러오고 합칠 기준 컬럼을 정해 두면, 처음에는 모든 행이 그대로 보입니다. 그 기준 컬럼 중 하나를 숨기거나 빼면 나머지 기준 값이 같은 행끼리 한 행으로 합쳐집니다. rollup: { by: ['country', 'name', 'company'] }에서 이름 컬럼을 숨기면 국가·회사마다 한 행이 되고, 다시 보이면 원래대로 나뉩니다. 행 그룹핑과는 다릅니다. 그룹핑은 모든 행을 그대로 두고 위에 그룹 행을 덧붙이지만, 여기서는 행 자체가 합쳐져서 행 수가 줄어듭니다.

aggFunc가 있는 컬럼은 합쳐진 행들의 집계 값을 보여 줍니다. 'sum'(합계), 'avg'(평균), 'min', 'max', 'count'(개수) 또는 직접 만든 함수를 쓸 수 있고, setColumnAggFunc()나 컬럼 메뉴의 집계 항목으로 바꿉니다. ID나 이메일처럼 그 밖의 컬럼은 합쳐진 행들의 값이 모두 같을 때만 그 값을 보여 주고, 다르면 빈칸으로 둡니다. 이런 컬럼 때문에 행이 안 합쳐지는 일은 없습니다. 기준은 by 컬럼 중 보이는 것만 셉니다. 컬럼 메뉴, 컬럼 선택 창, 사이드바, setColumnVisible()로 숨기든 setColumns()로 빼든 똑같이 동작합니다. 보이는 by 컬럼 값이 모두 같은 행은 처음부터 합쳐집니다.

합쳐진 행도 정렬, 필터, 그룹, 내보내기, 차트가 모두 됩니다. 그룹 행과 요약 행은 원래 행에서 다시 계산하므로 평균도 정확합니다. 합쳐진 행은 편집할 수 없고, getData()는 여전히 원본 행을 돌려줍니다. setRollup({ by })로 기준 컬럼을 바꾸고, setRollup(false)면 원본 행으로 돌아갑니다. rollup: true면 aggFunc가 없는 보이는 컬럼이 모두 기준이 됩니다. 브라우저에 있는 행에서 동작하며(datasource나 트리 데이터에서는 쓸 수 없음), 피벗 표를 보는 동안에는 피벗이 우선합니다.

rowSelection을 켜면 합쳐진 행을 고르는 것은 그 안의 원본 행을 고르는 것입니다. getSelectedRows()와 selectionChange 이벤트는 그 원본 행을 돌려주고(데이터에 있는 객체 그대로라 id를 서버에 보낼 수 있습니다), removeSelectedRows(), 합쳐진 행을 넘긴 removeRows(), 컨텍스트 메뉴의 행 삭제는 원본 행을 모두 지웁니다. 실행 취소하면 돌아옵니다. 선택한 행 복사와 exportCsv({ rows: 'selected' })는 화면에 보이는 합쳐진 행 그대로 나옵니다. getState().selected에는 보이는 행의 id가 들어가고, 합쳐진 행은 첫 원본 행의 id를 씁니다. 컬럼을 숨기거나 다시 보이게 하거나, 데이터가 바뀌거나, rollup을 켜고 끄면서 행이 다시 합쳐질 때도 선택은 원본 행을 따라갑니다. 새로 합쳐진 행은 그 안의 원본 행이 모두 선택돼 있었을 때만 선택되므로, 고르지 않은 행이 지워지는 일은 없습니다.

// Raw rows in; the rows merge by the by columns that are shown
const grid = createGrid(el, {
  columns: [
    { field: 'id' }, { field: 'country' }, { field: 'name' }, { field: 'company' }, { field: 'email' },
    { field: 'sales', type: 'number', aggFunc: 'sum' },
    { field: 'price', type: 'number', aggFunc: 'avg' },   // 'sum' | 'avg' | 'min' | 'max' | 'count' | (values, rows) => …
  ],
  data,
  rollup: { by: ['country', 'name', 'company'] },        // every row shows at first
});

grid.setColumnVisible('name', false);     // one row per country + company: sales summed, price averaged,
                                          // id and email shown only where the merged rows share them
grid.setColumnVisible('name', true);      // the rows split back
grid.setColumnAggFunc('price', 'max');    // the column menu has the same choices
grid.setRollup({ by: ['country'] });      // other by columns
grid.setRollup(false);                    // the original rows