Tomo
Tomo

Reputation: 434

How to iterate an array of arrays of objects with *ngFor in Angular 14?

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.

Upvotes: 0

Views: 1206

Answers (1)

Belouccio
Belouccio

Reputation: 290

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);
    }

Upvotes: 1

Related Questions