简体   繁体   中英

How to group array items properly

I have a table and I want to render headers grouped by group property from object in array. The array with the headers looks like this:

const headers = [
    { label: 'Date', group: '' },
    { label: 'Event', group: '' },
    { label: 'Days Out', group: '' },
    { label: 'T', group: 'Sales Velocity' },
    { label: '-1', group: 'Sales Velocity' },
    { label: '-2', group: 'Sales Velocity' },
    { label: '-3', group: 'Sales Velocity' },
    { label: '-4', group: 'Sales Velocity' },
    { label: 'Sold Last 5', group: 'Ticket Sales' },
    { label: 'Total Sold', group: 'Ticket Sales' },
    { label: 'Sellable cap.', group: 'Ticket Sales' },
    { label: '% sold', group: 'Ticket Sales' },
    { label: 'Availab.', group: 'Ticket Sales' },
    { label: 'Total', group: 'Revenue' },
    { label: 'Holds', group: 'Inventory Status' },
    { label: 'Comp', group: 'Inventory Status' },
    { label: 'Open', group: 'Inventory Status' },
    { label: 'Price cat.', group: 'Inventory Status' },
    { label: 'Avg. price', group: 'Stats' },
    { label: 'First time %', group: 'Stats' },
];

and the table component looks like this:

<TableHead>
    <TableRow>
      {headers.map((header, index) => (
        <TableCellASHeader key={index}>
          <TableSortLabel
            active={header.column === sorting.orderBy}
            direction={sorting.orderDirection}
            onClick={() => onClickSort(header.column)}
            IconComponent={ArrowDropDownIcon}
          >
            {/* {header.group} */} // here I need to render group but only once per group
            {header.label}
          </TableSortLabel>
        </TableCellASHeader>
      ))}
    </TableRow>
  </TableHead>

What I want is to render header.group above header.label but only once per group, like on this picture below. Any code sample will be appreciated. 在此处输入图像描述

First of all, I would club the headers into an object based on group.

const groupedHeaders = headers.reduce((acc, curr) => {
  let {group, label} = curr;
  if (!group) group = 'empty';
    if (!acc[group]) {
        acc[group] = [label]
    } else {
        acc[group] = [...acc[group], label]
    }
  return acc;
}, {});

After clubbing them up, the groupedHeaders would look like this -

{
  empty: [ 'Date', 'Event', 'Days Out' ],
  'Sales Velocity': [ 'T', '-1', '-2', '-3', '-4' ],
  'Ticket Sales': [
    'Sold Last 5',
    'Total Sold',
    'Sellable cap.',
    '% sold',
    'Availab.'
  ],
  Revenue: [ 'Total' ],
  'Inventory Status': [ 'Holds', 'Comp', 'Open', 'Price cat.' ],
  Stats: [ 'Avg. price', 'First time %' ]
}

Rendering in React part would make use of Object.entries() to iterate through the object and display accordingly.

Object.entries(groupedHeaders).map(([group, labels]) => {
  <TableRow>
      <HeaderGroup> // you will need to add css with flex/grid
         {group === 'empty' ? <EmptyHeader /> : <GroupHeader />}
         <SubHeaderGroup> // css required for grouping
           {labels.map((header, index) => (
             <TableCellASHeader key={index}>
               <TableSortLabel
                 active={header.column === sorting.orderBy}
                 ....
             </TableCellASHeader>
           ))}
         </SubHeaderGroup>
      </YourHeaderGroup>
   </TableRow>
});

Check out this Code Sandboxlink for the full version of code.

you can use new Set() and store group name in that and check each time when map function run. I have done this in javascript it will print group name 1 time and label many times., Check this

const headers = [
    { label: 'Date', group: '' },
    { label: 'Event', group: '' },
    { label: 'Days Out', group: '' },
    { label: 'T', group: 'Sales Velocity' },
    { label: '-1', group: 'Sales Velocity' },
    { label: '-2', group: 'Sales Velocity' },
    { label: '-3', group: 'Sales Velocity' },
    { label: '-4', group: 'Sales Velocity' },
    { label: 'Sold Last 5', group: 'Ticket Sales' },
    { label: 'Total Sold', group: 'Ticket Sales' },
    { label: 'Sellable cap.', group: 'Ticket Sales' },
    { label: '% sold', group: 'Ticket Sales' },
    { label: 'Availab.', group: 'Ticket Sales' },
    { label: 'Total', group: 'Revenue' },
    { label: 'Holds', group: 'Inventory Status' },
    { label: 'Comp', group: 'Inventory Status' },
    { label: 'Open', group: 'Inventory Status' },
    { label: 'Price cat.', group: 'Inventory Status' },
    { label: 'Avg. price', group: 'Stats' },
    { label: 'First time %', group: 'Stats' },
];

let groups = new Set();
headers.map((header) => {
    if(!groups.has(header.group)){
      groups.add(header.group);
      console.log(header.group); 
    } 
    console.log(header.label);
})

Output

Date
Event
Days Out
Sales Velocity
T
-1
-2
-3
-4
Ticket Sales
Sold Last 5
Total Sold
Sellable cap.
% sold
Availab.
Revenue
Total
Inventory Status
Holds
Comp
Open
Price cat.
Stats
Avg. price
First time %

You can alternatively use lodash/uniqBy to achieve this. If you are working on enterprise level application and using React/Angular, it's worth considering. You can achieve the desired result in one line like below-

import uniqBy from ‘lodash/uniqBy’;

const modifiedArray = uniqBy(headers,’group’);

Also, it is always recommended to check beforehand if your data is undefined or not. So, the best way to do is like below-

import get from ‘lodash/get’;

const modifiedArray = uniqBy((get(headers, ‘response.data’, [])),’group’);

Considering headers array is part of your api and the path is response.data.headers.

Please Note: You need to install lodash as your dev dependency in your project. uniqBy method will show only unique values without any duplication.

for example this code with filter and map and optionally put arrays into object

 const headers = [{ label: 'Date', group: '' }, { label: 'Event', group: '' }, { label: 'Days Out', group: '' }, { label: 'T', group: 'Sales Velocity' }, { label: '-1', group: 'Sales Velocity' }, { label: '-2', group: 'Sales Velocity' }, { label: '-3', group: 'Sales Velocity' }, { label: '-4', group: 'Sales Velocity' }, { label: 'Sold Last 5', group: 'Ticket Sales' }, { label: 'Total Sold', group: 'Ticket Sales' }, { label: 'Sellable cap.', group: 'Ticket Sales' }, { label: '% sold', group: 'Ticket Sales' }, { label: 'Availab.', group: 'Ticket Sales' }, { label: 'Total', group: 'Revenue' }, { label: 'Holds', group: 'Inventory Status' }, { label: 'Comp', group: 'Inventory Status' }, { label: 'Open', group: 'Inventory Status' }, { label: 'Price cat.', group: 'Inventory Status' }, { label: 'Avg. price', group: 'Stats' }, { label: 'First time %', group: 'Stats' }, ]; const empty = headers.filter(el => el.group === '').map(el => el.label) console.log('empty', empty) const sales = headers.filter(el => el.group === 'Sales Velocity').map(el => el.label) console.log('sales', sales) const ticket = headers.filter(el => el.group === 'Ticket Sales').map(el => el.label) console.log('ticket', ticket) const revenue = headers.filter(el => el.group === 'Revenue').map(el => el.label) console.log('revenue', revenue) const inventory = headers.filter(el => el.group === 'Inventory Status').map(el => el.label) console.log('inventory', inventory) const stats = headers.filter(el => el.group === 'Stats').map(el => el.label) console.log('stats', stats) const obj = {} obj.empty = empty obj.sales = sales obj.ticket = ticket obj.revenue = revenue obj.inventory = inventory obj.stats = stats console.log('obj', obj)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM