0

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.

4
  • In your case I'd recommend to use nested loops. The first one is *ngFor="let items of pairsByDraw" and the nested one is *ngFor="let item of items". You didn't provide your pipe code so it's hard to say what it's doing. Well, implement it relatively to the right loop. Commented Oct 10, 2022 at 18:39
  • will you have in your data person3 and more? Commented Oct 10, 2022 at 19:02
  • @DmitryS. I already tried that, but the 2nd *ngFor won't work. I don't have a pipe code, I only tried with the build-in keyvalue. Commented Oct 10, 2022 at 19:03
  • @Belouccio Actually I have from 3 to 5 persons, but no more than 5. Draws will be many... Commented Oct 10, 2022 at 19:09

1 Answer 1

1

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);
    }
Sign up to request clarification or add additional context in comments.

2 Comments

Sorting is by draw (desc) and inside the draw by id (asc). It won't work in my project because of errors like Element implicitly has an 'any' type because type 'Map<any, any>' has no index signature. Did you mean to call 'map.get'?, but I managed to make it work in [stackblitz.com/edit/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.