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.

Tomo
  • 429
  • 1
  • 10
  • 24
  • 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. – Dmitry S. Oct 10 '22 at 18:39
  • will you have in your data person3 and more? – Belouccio Oct 10 '22 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`. – Tomo Oct 10 '22 at 19:03
  • @Belouccio Actually I have from 3 to 5 persons, but no more than 5. Draws will be many... – Tomo Oct 10 '22 at 19:09

1 Answers1

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>

ts

public group() {
        const groupBy = (data) => {
            // Create a dictionnary 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);

    }
Belouccio
  • 300
  • 1
  • 3
  • 14
  • 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' has no index signature. Did you mean to call 'map.get'?`, but I managed to make it work in [https://stackblitz.com/edit/display-json-file-ex1bd6?file=src%2Fassets%2Fpairs.json,src%2Fapp%2Fconfig.service.ts,src%2Fapp%2Fapp.component.ts%3AL23-L23,src%2Fapp%2Fapp.component.html"][1] – Tomo Oct 11 '22 at 19:08
  • https://stackblitz.com/edit/display-json-file-ebnrvc?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.component.html so fit? – Belouccio Oct 11 '22 at 19:57