0

I'm using Angular 14.

I have an Observable that is an Array of Objects, looks like a dictionary with key/values.

Array: [
  0: Object {id: 100, manufacturer: 'ford', model: 'mustang'},
  1: Object {id: 110, manufacturer: 'ford', model: 'fiesta'},
  2: Object {id: 150, manufacturer: 'ford', model: 'escort'},
  3: Object {id: 320, manufacturer: 'Toyota', model: 'camry'},
  4: Object {id: 325, manufacturer: 'Toyota', model: 'rav4'},
  5: Object {id: 345, manufacturer: 'Toyota', model: 'corolla'},

]

I want to group this array by manufacturer. I need two separate lists for Ford and Toyota.

I have read about the RxJs .groupBy() but I can't get it to work as this is a dictionary.

this.cars$.pipe(
        groupBy(g => g[??????].manufacturer),
        mergeMap(group => group.pipe(toArray()))
      ).subscribe(s => { .... });

I have also read about the Javascript reduce() function but it is difficult to understand and I can't find an example of it reducing a dictionary.

Any help appreciated!

Scottish Smile
  • 455
  • 7
  • 22
  • I think the issue is that g in your groupBy is the whole array. groupBy as a pipe operator works on a series of emissions – Andrew Allen Apr 17 '23 at 07:44
  • about a reduce, think in this as a "confortable way" to make a loop, see a example in this [SO](https://stackoverflow.com/questions/68941600/aggregate-values-and-group-by-date-in-angular/68942961#68942961). An example of reduce to group in [this another SO](https://stackoverflow.com/questions/74452136/angular-nested-loop-duplicates/74457442#74457442) – Eliseo Apr 17 '23 at 07:59
  • Yeah, I don't think groupBy() can do what I want for exactly that (it's a whole array in there). Reduce() was the winner! – Scottish Smile Apr 18 '23 at 01:16

1 Answers1

2

To group your array of objects by manufacturer, you can use the reduce() method to create a new object with keys for each manufacturer and values as arrays of cars.

Here's an example implementation:

this.cars$.pipe(
  map(cars => cars.reduce((acc, car) => {
    if (!acc[car.manufacturer]) {
      acc[car.manufacturer] = [];
    }
    acc[car.manufacturer].push(car);
    return acc;
  }, {})),
).subscribe(manufacturers => {
  const fordCars = manufacturers['ford'];
  const toyotaCars = manufacturers['Toyota'];
  // Do something with the grouped arrays
});

Explanation:

  • map() operator is used to transform the original Observable of cars array into a new Observable that emits an object with manufacturers as keys and arrays of cars as values.
  • reduce() method is used to iterate through each car in the array and add it to the corresponding manufacturer array in the accumulator object.
  • The accumulator object starts as an empty object {}.
  • If the manufacturer array doesn't exist yet, it's initialized with an empty array.
  • The acc (accumulator) object is returned at the end of each iteration, which builds up the final grouped object.
  • The subscribe() method is used to consume the resulting object and extract the arrays for Ford and Toyota, which can be used for further processing.

Hope this helps!

  • Thanks, I'll give it a shot! I didn't try acc[car.manufacturer] I have my fingers and toes crossed for luck :) – Scottish Smile Apr 17 '23 at 11:59
  • Yes this worked! A quick note for others - you need the trailing {} in the reduce statement. It is for setting the initial value. Without it you will get "TypeError: Reduce of empty array with no initial value" – Scottish Smile Apr 18 '23 at 01:11