【问题标题】:How to map and reduce child values from FirebaseListObservable如何从 FirebaseListObservable 映射和减少子值
【发布时间】:2017-02-21 18:14:05
【问题描述】:

我正在尝试总结考试成绩,我的数据看起来像这样

   { testResult: 
      { foo:
         {foo1: x},
         {foo2: y}
      },
      { bar:
         {bar1: z}

'foo' 和 'bar' 是有意义的问题子类别,子节点包含 questionID 和相应的分数。我需要总结按类别排序的分数。我这样查询列表

let res = this._dbref.database.list('/bmzi-answers',{
      query: {
        orderByKey: true,
        equalTo: id
      }
    })

并用{{ res | async | json }} 显示结果告诉我,查询有效。我得到类似的东西

[{"cat1":3, "cat2":3, "cat3":3, "$key":"cat"}]

如何减少组件逻辑中的值,以获得所有标记为 cat* 的问题的总和?

我了解过滤器/映射/减少等概念,但我是 Reactive Javascript、Angular2 和 AngularFire 的新手。

请帮助并提前感谢您!

编辑: 所有孩子都有猫前缀,这是域/问卷的一部分。这段代码

  let res = this.dbRef.database.list(`/bmzi-answers/${id}`)
                  .do((array) => { console.log(JSON.stringify(array)); })
                  .map((array) => array
                      .reduce((acc, element) => acc + element.$value, 0)
            )
  let score = res.subscribe(sum => {
                  console.log("score: " + sum)
              });
  return score;

在第一次调用时产生这个控制台输出:

[
    { "$key": "cat1", "$value": 3 },
    { "$key": "cat2", "$value": 3 },
    { "$key": "cat3", "$value": 3 }
]
score: [object Object]

这是第二次:

[]  
score: undefined  
[{"$value":3,"$key":"fitges1"}]
score: undefined  
[{"$value":3,"$key":"fitges1"},{"$value":3,"$key":"fitges2"}]
score: undefined 
[{"$value":3,"$key":"fitges1"},{"$value":3,"$key":"fitges2"},{"$value":3,"$key":"fitges3"}]  
score: undefined 

【问题讨论】:

    标签: javascript angular firebase firebase-realtime-database angularfire2


    【解决方案1】:

    您的示例输出表明以下数据:

    {
        "bmzi-answers": {
            "cat": {
                "cat1": 3,
                "cat2": 3,
                "cat3": 3
            }
        }
    }
    

    您的查询使用orderByKeyequalTo 来获取一个可观察的列表,该列表发出一个数组。如果键存在,查询将只返回一个包含单个对象的数组。将键合并到查询路径中会更简单,如下所示:

    let res = this._dbref.database.list(`/bmzi-answers/${id}`);
    

    此查询返回的 observable 将是一个包含键的子项的数组(以 AngularFire 方式表示):

    [
        { "$key": "cat1", "$value": 3 },
        { "$key": "cat2", "$value": 3 },
        { "$key": "cat3", "$value": 3 }
    ]
    

    RxJS map 运算符可用于将发出的数组转换为另一个值,并且可以使用您所熟悉的 Array.proptype 函数执行转换:

    let id = "cat";
    let regExp = new RegExp(`${id}\\d+`);
    let res = this._dbref.database
        .list(`/bmzi-answers/${id}`)
        .map((array) => array
            .filter((element) => regExp.test(element.$key))
            .reduce((acc, element) => acc + element.$value, 0)
        );
    

    如果所有孩子都有cat 前缀,则不需要filter,但您的问题并不清楚是否是这种情况。

    【讨论】:

    • 这几乎解决了问题,但是当我订阅 res 时,let score = res.subscribe(sum => console.log(sum)) 的值是未定义的,但第一次评估的唯一分数。我如何保证订阅 res 时减少完成?
    • reduce 是同步的,所以这不是问题。如果您在 map 运算符之前添加 do((array) => { console.log(JSON.stringify(array)); }),那么第一个发出的数组值会导致 undefined 得分?
    • 是 [{"$value":3,"$key":"fitges1"},{"$value":3,"$key":"fitges2"},{"$value ":3,"$key":"fitges3"}].
    • 我不知道是否合适,但我会发布答案以显示代码。
    • 应该是console.log("score: " + sum)score 变量保存订阅对象。
    【解决方案2】:

    流发出数据。一个 observable 就是一个流。

    例如:

    let stream = Rx.Observable.from([3,6,9]) 
    

    这段代码创建了一个可观察的流,它发出 3 然后 6 然后 9

    现在假设我们想将这些值加倍:

    let stream = Rx.Observable.from([3,6,9]).map((value) => {return value*2})
    

    现在流发出 6、12、18。

    我们终于可以做到了:

    let stream1 = Rx.Observable.from([3,6,9])
    let stream2 = stream1.map((value) => {return value*2})
    

    现在我们可以订阅 stream1 或 stream2 或两者。

    PS .reduce() 函数也可以与 es5 Array.reduce 函数一样使用。你可以对流做同样的事情,就像对数组一样。

    数组是内存中的序列。流是随时间变化的序列。

    这就是为什么我们可以轻松地将数组转换为流的原因。我们可以在每次发射之间设置间隔。等等

    【讨论】:

    • 这有助于掌握“更大的图景”,并提醒我订阅转换后的 observable。 ;) 非常感谢,上帝也保佑你!
    猜你喜欢
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多