Here is a fully working solution with a Stackblitz Demo with merging of similar cells.
You have to calculate a row span
for each item and bind it to the rowspan
attribute of the td
. You also have to conditionally render the td
to display it only for the first item for each state.
For this, you can create a preprocessed array, ordered by state and by county, with added span
properties for states and counties.
To set the span
properties, you can count the number of children for each state and each county for the state by filtering the original array.
The goal is to get a array like this:
[
{state: "CA", county: "2", item: 0.019, stateSpan: 3, countySpan: 2},
{state: "CA", county: "2", item: 0.037, stateSpan: 0, countySpan: 0},
{state: "CA", county: "3", item: 0.14, stateSpan: 0, countySpan: 1},
{state: "MN", county: "1", item: 0.297, stateSpan: 4, countySpan: 3},
{state: "MN", county: "1", item: 0.04, stateSpan: 0, countySpan: 0},
{state: "MN", county: "1", item: 0.0374, stateSpan: 0, countySpan: 0},
{state: "MN", county: "3", item: 0.14, stateSpan: 0, countySpan: 1}
]
Here is the code:
<table>
<tr>
<th>State</th>
<th>County</th>
<th>Item</th>
</tr>
<tr *ngFor="let item of dataExt">
<td [attr.rowspan]="item.stateSpan" *ngIf="item.stateSpan">{{ item.state }}</td>
<td [attr.rowspan]="item.countySpan" *ngIf="item.countySpan">{{ item.county }}</td>
<td>{{ item.item }}</td>
</tr>
</table>
export class AppComponent {
data = [
{ state: 'MN', county: '1', item: 0.297 },
{ state: 'MN', county: '1', item: 0.04 },
{ state: 'MN', county: '3', item: 0.14 },
{ state: 'CA', county: '2', item: 0.019 },
{ state: 'MN', county: '1', item: 0.0374 },
{ state: 'CA', county: '2', item: 0.037 },
{ state: 'CA', county: '3', item: 0.14 }
];
dataExt: any[] = [];
constructor() {
this.processData();
}
private processData() {
const statesSeen = {};
const countiesSeen = {};
this.dataExt = this.data.sort((a, b) => {
const stateComp = a.state.localeCompare(b.state);
return stateComp ? stateComp : a.county.localeCompare(b.county);
}).map(x => {
const stateSpan = statesSeen[x.state] ? 0 :
this.data.filter(y => y.state === x.state).length;
statesSeen[x.state] = true;
const countySpan = countiesSeen[x.state] && countiesSeen[x.state][x.county] ? 0 :
this.data.filter(y => y.state === x.state && y.county === x.county).length;
countiesSeen[x.state] = countiesSeen[x.state] || {};
countiesSeen[x.state][x.county] = true;
return { ...x, stateSpan, countySpan };
});
}
}
Here is the resulting table:
