1

I have an array containing 4 fields and have to group by two properties and aggregate (sum) one of the field from the grouped array.

tried to group by more than one is not working.

the below code I tried to manipulate the array to the expected list, but not sure how to achieve the below expected result using RxJs

from(this.dummyData)
        .pipe(
          groupBy(
            function (e) {
                return { category: e.category, manDate: e.manDate };   //is this valid at all?
            },
            function (e) { return e; }), 
            mergeMap(group => zip(of(group.key), group.pipe(toArray())))
        )
        .subscribe( function(result) {
          console.log(result);
        });

Existing Array:

[{
        category : "Printer",
        manDate : "02/01/2019",
        amount : 90            
      },
      {
        category : "Printer",
        manDate : "02/01/2019",
        amount : 100            
      },
      {
        category : "Printer",
        manDate : "02/03/2019",
        amount : 90            
      },
      {
        category : "Printer",
        manDate : "02/04/2019",
        amount : 90            
      },
      {
        category : "Scanner",
        manDate : "08/21/2019",
        amount : 8            
      },
      {
        category : "Scanner",
        manDate : "08/21/2019",
        amount : 25            
      },
      {
        category : "Scanner",
        manDate : "08/21/2019",
        amount : 20            
      },
      {
        category : "Scanner",
        manDate : "08/21/2019",
        amount : 10            
      }

    ];

expected :

[{
            category : "Printer",
            subCategory : "A",
            manDate : "02/01/2019",
            amount : 190            
          },{
            category : "Printer",
            subCategory : "A",
            manDate : "02/03/2019",
            amount : 90            
          },{
            category : "Printer",
            subCategory : "A",
            manDate : "02/04/2019",
            amount : 90            
          },{
            category : "Scanner",
            subCategory : "A",
            manDate : "08/21/2019",
            amount : 63            
          }]

I need anyone's help to acheive this result.

4 Answers 4

2
from(dummyData)
  .pipe(
    groupBy(// Group them by category and return the appropriate Arrays
      val => val.category
    ),
    mergeMap(group => {
      return group.pipe(toArray());
    }),
    mergeMap((array) => {// Take each from above array and group each array by manDate
      return from(array).pipe(groupBy(
        val => val.manDate,
        ),
        mergeMap(group => {
          return group.pipe(toArray()); // return the group values as Arrays
        })
      );
    }),
    map((val) => { //For each array returned , calculate the sum and map it to the Object you wanted
      let amount = 0;
      val.map(v => {
        amount = amount + v.amount;
      });
      return {category: val[0].category, manDate: val[0].manDate, amount};
    }),
    toArray() //finally combine all returned objects to an array
  ).subscribe(
    val => console.log(val)
);

It was hard to wrap my head around it but it seems to work. If it helped , dont forget to upvote, thanks!

Sign up to request clarification or add additional context in comments.

2 Comments

map((val) => , there is an error at map function, could you please correct it
@Gopinath2105 where?
2

Both the accepted and the current alternative RxJS solution make use of toArray() operator. As an alternative, we can work directly on the data stream:

from(dummyData)
  .pipe(
    groupBy(d => d.category),
    mergeMap(byCategory =>
      byCategory.pipe(
        groupBy(d => d.manDate),
        mergeMap(byDate =>
          byDate.pipe(
            reduce((dateAggs, x) => dateAggs + x.amount, 0),
            map(x => ({
              date: byDate.key,
              amount: x,
              category: byCategory.key,
            }))
          )
        )
      )
    ),
    toArray() // Comment out if you'll keep working on the stream
  )
  .subscribe(x => console.log(x));

Demo.

Comments

1

Try this method, see if it fits your needs. You don't need Rxjs grouping IMO. And there probably are more ways of manipulating the input array for your desired output, but this is my relatively fast and easy to read solution. Let me know how it fits your case :)

  transform(array: Array<any>) {
    const newArray = [];
    array.forEach(item => {
      const index = newArray.findIndex(
        i => i.category === item.category && i.manDate === item.manDate
      );
      if (index >= 0) {
        newArray[index].amount += item.amount;
      } else {
        newArray.push(item);
      }
    });
    return newArray;
  }

3 Comments

The question specifically says with rxjs and learning Rxjs is a crucial skill when using angular. Resorting to the imperative mindset will be a drawback when you will have to solve problems using only rxjs reactive programming paradigm.
@Jensen Indeed learning Rxjs is a crucial skill for an Angular developer, but knowing when to use which tools is as important (that is, generally for a developer). Sometimes it might not be necessary casting an Array to an Observable, manipulating it using Rxjs, then subscribing to the Observable, when simpler solutions can make it. If the above question is about learning Rxjs, then it's not the best way to learn Rxjs. If it is the functionality the user was seeking, then I provided a simpler solution than Rxjs. :)
From this example itself, I've just learned how to use the group pipe and how to use toArray() effectrively. In my opinion trying out things reactively is a must else when you will face a problem which can't be solved imperatively, you will be stuck. Again this switch from imperative to reactive mindset wont come easily and requires lots of practice. I dont get why the question specifcally ask 'using rxjs' if not for the purpose of learning? Its clear it has nothing to do with event-base or asynchronous data handling.
1

I think we can use here reduce.

from(dummyData)
.pipe(
groupBy( val => val.category ), // Group them by category and return the appropriate Arrays
mergeMap(group => { return group.pipe(toArray()); }),
mergeMap((array) => {// Take each from above array and group each array by manDate
  return from(array).pipe( 
    groupBy( val => val.manDate, ),
    mergeMap(group => {
      return group.pipe(toArray()); // return the group values as Arrays
    })
  );
}),
mergeMap((array) => {// Take each from above array and group each array by manDate
  return from(array).pipe( 
    reduce((acc, curr) => ({
        category: curr.category, 
        manDate: curr.manDate,
        amount: curr.amount + acc.amount
    }), { category: '', manDate: '', amount: 0})
  );
}),   
).subscribe(
val => console.log(val));

1 Comment

hi and welcome to StackOverflow! there is already an accepted answer to this question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.