I know similar questions were asked, but none of them helped me with my problem.
I have a dynamic array of objects I get from server (more draws in the future):
0: {id: 19, draw: 8, colour: 'Black', person1: 'John', person2: 'Jane'}
1: {id: 18, draw: 8, colour: 'Red', person1: 'Mike', person2: 'Mary'}
2: {id: 20, draw: 8, colour: 'White', person1: 'Sam', person2: 'Leah'}
3: {id: 16, draw: 7, colour: 'Red', person1: 'Bob', person2: 'Nora'}
4: {id: 17, draw: 7, colour: 'Black', person1: 'Tom', person2: 'Tina'}
I reorder the array into multiple arrays based on draw number:
getDrawHistory(): void {
this.pairApiService.getDrawHistory().subscribe(pairs => {
this.pairs = pairs;
const groupBy = (key: string) => (array: any[]) =>
array.reduce((objectsByKeyValue: { [x: string]: any; }, obj: { [x: string]: any; }) => {
const value = obj[key];
objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
return objectsByKeyValue;
}, {})
const groupByDraw = groupBy('draw');
this.pairsByDraw = JSON.parse(JSON.stringify({ pairsByDraw: groupByDraw(this.pairs) }, null, 2));
})
}
Where I get:
pairsByDraw:
7: Array(2)
0: {id: 16, draw: 7, colour: 'Red', person1: 'Bob', person2: 'Nora'}
1: {id: 17, draw: 7, colour: 'Black', person1: 'Tom', person2: 'Tina'}
8: Array(3)
0: {id: 18, draw: 8, colour: 'Red', person1: 'Mike', person2: 'Mary'}
1: {id: 19, draw: 8, colour: 'Black', person1: 'John', person2: 'Jane'}
2: {id: 20, draw: 8, colour: 'White', person1: 'Sam', person2: 'Leah'}
What I want is to iterate the array of arrays of objects with *ngFor
and get something like (possibly dynamically generated mutliple tables):
Red | Black |
---|---|
Bob | Tom |
Nora | Tina |
Red | Black | White |
---|---|---|
Mike | John | Sam |
Mary | Jane | Leah |
How is it done?
I tried:
*ngFor="let item of pairsByDraw.pairsByDraw | keyvalue"
{{item.key}}: {{item.value}}
but cannot get anything from item.value
other than [object Object]
.
EDIT: Changing the brackets from in:
const groupBy = (key: string) => (array: any[]) =>
array.reduce((objectsByKeyValue: { [x: string]: any; }, obj: { [x: string]: any; }) => {
const value = obj[key];
objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
return objectsByKeyValue;
}, []) // before: }, {})
from {}
to []
seems to make some difference. Will dig into.
Maybe not the best solution, but it works (except sort, I don't know why it doesn't sort by color).
HTML:
<table *ngFor="let table of tables">
<tr>
<th *ngFor="let column of table.columns">
{{column}}
</th>
</tr>
<tr *ngFor="let personLevel of table.personsLevels">
<td *ngFor="let column of table.columns">
{{personLevel[column]}}
</td>
</tr>
</table>
Typescript:
public group() {
const groupBy = (data) => {
// Create a dictionary of groups.
const map = new Map();
data.forEach(item => {
const groupKey = item['draw'];
const table = map.get(groupKey);
// first we group by draw, it will be our tables
if (table == null) {
const newTable = {
columns: [item.colour], // we also need columns
personsLevels: groupPersonsByLevels(item, item.colour) // and we need rows
}
map.set(groupKey, newTable);
} else {
table.columns.push(item.colour);
table.personsLevels = groupPersonsByLevels(item, item.colour, table.personsLevels);
};
})
for (const key in map) {
map[key].personsLevels.sort((a, b) => a.level - b.level);
map[key].columns.sort((a, b) => a - b);
}
// Transform it back into a list of groups.
return [...map.values()];
}
// rows is the persons group by person level
// (if we have 3 levels for example it would be 3 rows.
// Each element in rows puts in his own color
const groupPersonsByLevels = (item, column, personsLevels = []) => {
for (const key in item) {
if (key.match('person')) {
const level = key.replace( /^\D+/g, '');
const existingPersonLevel = personsLevels.find(el => el.level === level);
if (existingPersonLevel) {
existingPersonLevel[column] = item[key];
} else {
personsLevels.push({
level,
[column]: item[key]
});
};
}
}
return personsLevels;
}
this.tables = groupBy(this.pairs);
}