【问题标题】:How to groupby by two fields from array using rxjs?如何使用 rxjs 按数组中的两个字段分组?
【发布时间】:2019-11-04 05:47:29
【问题描述】:

我有一个包含 4 个字段的数组,并且必须按两个属性分组并聚合(求和)分组数组中的一个字段。

尝试按多个分组无效。

以下代码我尝试将数组操作到预期列表,但不确定如何使用 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);
        });

现有数组:

[{
        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            
      }

    ];

预期:

[{
            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            
          }]

我需要任何人的帮助来实现这个结果。

【问题讨论】:

    标签: angular typescript rxjs


    【解决方案1】:
    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)
    );
    

    我很难理解它,但它似乎有效。如果有帮助别忘了点赞,谢谢!

    【讨论】:

    • map((val) => ,map函数有错误,请指正
    • @Gopinath2105 在哪里?
    【解决方案2】:

    试试这个方法,看看它是否符合你的需要。您不需要 Rxjs 对 IMO 进行分组。并且可能有更多方法可以为您想要的输出操作输入数组,但这是我相对快速且易于阅读的解决方案。让我知道它如何适合您的情况:)

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

    【讨论】:

    • 这个问题特别提到了 rxjs,学习 Rxjs 是使用 Angular 时的一项关键技能。当您必须仅使用 rxjs 反应式编程范式解决问题时,诉诸命令式思维方式将是一个缺点。
    • @Jensen 确实,学习 Rxjs 对 Angular 开发人员来说是一项至关重要的技能,但知道何时使用哪些工具同样重要(也就是说,通常对开发人员而言)。有时可能不需要将 Array 转换为 Observable,使用 Rxjs 对其进行操作,然后订阅 Observable,而更简单的解决方案可以做到这一点。如果上面的问题是关于学习 Rxjs 的,那么这并不是学习 Rxjs 的最佳方式。如果是用户想要的功能,那么我提供了比 Rxjs 更简单的解决方案。 :)
    • 从这个例子本身,我刚刚学会了如何使用组管道以及如何有效地使用 toArray()。在我看来,当你面临一个无法强制解决的问题时,你必须被动地尝试事情,否则你会被卡住。再一次,从命令式到被动式思维的转变并不容易,需要大量的练习。如果不是为了学习,我不明白为什么这个问题会专门问“使用 rxjs”?很明显,它与基于事件或异步数据处理无关。
    【解决方案3】:

    我认为我们可以在这里使用 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));
    

    【讨论】:

    • 嗨,欢迎来到 StackOverflow!这个问题已经有一个公认的答案。
    【解决方案4】:

    接受的和当前的替代 RxJS 解决方案都使用 toArray() 运算符。作为替代方案,我们可以直接处理数据流:

    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.

    【讨论】:

      猜你喜欢
      • 2014-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-20
      • 1970-01-01
      • 2021-06-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多