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.
enter image description here
-
filter the arraygrzesiekmq– grzesiekmq2020年07月03日 11:50:24 +00:00Commented Jul 3, 2020 at 11:50
-
2You might want to take a look at this answer, or if you don't mind using libraries, you could try Lodash's groupBy() method.Cas Dekkers– Cas Dekkers2020年07月03日 11:52:51 +00:00Commented Jul 3, 2020 at 11:52
-
and make new arrays with given type I mean Sales Velocity ['T','-1', '-2', '-3', '-4'] Ticket Sales[] Revenue[] Inventory Status[] Stats[] (I skipped the data)grzesiekmq– grzesiekmq2020年07月03日 11:56:16 +00:00Commented Jul 3, 2020 at 11:56
-
@grzesiekmq can you please write a sample of how to do this? thanksMilos– Milos2020年07月03日 11:58:46 +00:00Commented Jul 3, 2020 at 11:58
-
ok I wrote codegrzesiekmq– grzesiekmq2020年07月03日 13:26:41 +00:00Commented Jul 3, 2020 at 13:26
4 Answers 4
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 Sandbox link for the full version of code.
3 Comments
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 %
1 Comment
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.
Comments
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)