【问题标题】:Dynamically calculate the average for a nested collection using lodash使用 lodash 动态计算嵌套集合的平均值
【发布时间】:2017-05-11 19:41:56
【问题描述】:

我有一个 JSON 对象数组(一个集合),例如:

[{
  "x": {
        "x1": 1
  },
  "y": {
    "yt": 0,
    "zt": 4,
    "qa": 3,
    "ft": 0,
    ...
  }
},
{
  "x": {
        "x1": 5
  },
  "y": {
    "yt": 10,
    "zt": 2,
    "qa": 0,
    "ft": 0,
    ...
  }
}]

我想计算每个字段的平均值。结果结构应该相同。喜欢:

    {
      "x": {
            "x1": 3
      },
      "y": {
        "yt": 5,
        "zt": 3,
        "qa": 1.5,
        "ft": 0,
        ...
      }
    }

谢谢

【问题讨论】:

    标签: javascript json lodash


    【解决方案1】:

    您可以使用扩展语法和 lodash 的 _.mergeWith() 合并对象。 合并时,如果第二个参数 (b) 是一个数字,则将其除以原始数组中的项目数,以获得它各自对总平均值的贡献。如果第一个参数(a)是数字,直接相加不除(避免多次相除),如果未定义则加0。

    我添加了 2 个对象数组和 3 个对象数组的示例。

    const getAvg = (data) => _.mergeWith({}, ...data, (a, b) => {
      if(_.isNumber(b)) {
        return ((b || 0) / data.length) + (_.isNumber(a) ? (a || 0) : 0);
      }
    });
    
    const data1 = [
    {"x":{"x1":1},"y":{"yt":0,"zt":4,"qa":3,"ft":0}},
    {"x":{"x1":5},"y":{"yt":10,"zt":2,"qa":0,"ft":0}}
    ];
    
    const data2 = [
    {"x":{"x1":1},"y":{"yt":0,"zt":4,"qa":3,"ft":0}},
    {"x":{"x1":5},"y":{"yt":10,"zt":2,"qa":0,"ft":0}},
    {"x":{"x1":3},"y":{"yt":2,"zt":6,"qa":3,"ft":0}}
    ];
    
    const result1 = getAvg(data1);
    
    console.log('2 objects in the array: ', result1);
    
    const result2 = getAvg(data2);
    
    console.log('3 objects in the array: ', result2);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

    【讨论】:

    • 这是一个好方法!我使用 data.map((arr) => arr.ratings );过滤。因为还有其他 NaN 字段。我也可以使用上述功能吗? Lodash 地图不会返回与内置地图功能相同的数据。
    • NaN 函数是实际的 NaN 还是只是对象、字符串等其他东西......?如果是这样,它们将定期合并。如果他们的实际NaN,将不得不处理他们,因为NaN 在javascript 中被认为是一个数字。无论如何,_.mergeWith() 不会过滤属性。
    • 这是一个文本。不过,它也可能是一个实际的 NaN。
    【解决方案2】:

    您可以首先收集和汇总同一数据结构中的所有值,然后通过除以给定数组的长度来计算平均值。

    function getParts(array, result) {
        function iter(o, r) {
            Object.keys(o).forEach(function (k) {
                if (o[k] && typeof o[k] === 'object') {
                    return iter(o[k], r[k] = r[k] || {});
                }
                r[k] = (r[k] || 0) + o[k];
            });
        }
    
        function avr(o) {
            Object.keys(o).forEach(function (k) {
                if (o[k] && typeof o[k] === 'object') {
                    return avr(o[k]);
                }
                o[k] = o[k] /data.length;
            });
        }
    
        data.forEach(function (a) {
            iter(a, result);
        });
        avr(result);
    }
    
    var data = [{ x: { x1: 1 }, y: { yt: 0, zt: 4, qa: 3, ft: 0, } }, { x: { x1: 5 }, y: { yt: 10, zt: 2, qa: 0, ft: 0, } }],
        result = {};
    
    getParts(data, result);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • 谢谢@Nina。已经 3 年多了,这仍然是目前最优雅的答案。
    【解决方案3】:

    let objectArray = [{
      "x": {
            "x1": 1
      },
      "y": {
        "yt": 0,
        "zt": 4,
        "qa": 3,
        "ft": 0,
      }
    },
    {
      "x": {
            "x1": 5
      },
      "y": {
        "yt": 10,
        "zt": 2,
        "qa": 0,
        "ft": 0,
      }
    }];
    
    function findAverage(array) {
      
      let counter = {},
        result = {},
        i,
        obj,
        key,
        subKey;
      
      // Iterate through array
      for (i = 0; i < array.length; i++) {
        obj = array[i];
        // Copy each key in array element to counter object
        for (key in obj) {
          counter[key] = counter[key] || {};
          // Increment and keep count of key-values of counter based on values in array element
          for (subKey in obj[key]) {
            counter[key][subKey] = counter[key][subKey] || {total: 0, numElements: 0};
            counter[key][subKey].total += obj[key][subKey];
            counter[key][subKey].numElements += 1;
          }
        }
      }
      // Go back through counter to find average of all existing subkeys (based on incremented total and the number of elements recorded) and throw it into result object
      for (key in counter) {
        result[key] = result[key] || {};
        
        for (subKey in counter[key]) {
          result[key][subKey] = counter[key][subKey].total / counter[key][subKey].numElements;
        }
      }
      
      return result;
    }
    
    console.log(findAverage(objectArray));

    设计不是绝对最优的,复制对象可以在事先不知道其结构的情况下递归完成,但我希望尽可能保持步骤清晰。

    已编辑以允许作为 sn-p 进行测试。不知道你甚至可以在 SO 上做到这一点!

    【讨论】:

      【解决方案4】:

      var array = [{
        "x": {
              "x1": 1
        },
        "y": {
          "yt": 0,
          "zt": 4,
          "qa": 3,
          "ft": 0
        }
      },
      {
        "x": {
              "x1": 5
        },
        "y": {
          "yt": 10,
          "zt": 2,
          "qa": 0,
          "ft": 0
        }
      }];
      function aintob(){
        var o = {};
        var first = array[0],
            second = array[1];
        var result = {x:{},y:{}};
        var each = function(letter, oa, ob){
          var i,
          letter = {};
          for(i in oa){
            letter[i] = (oa[i]+ob[i])/2;
          }
          return letter;
        }
        o.x = each("x", first.x, second.x);
        o.y = each("y", first.y, second.y);
        return o;
      }
      console.log(aintob());

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-31
        • 1970-01-01
        • 2018-08-25
        • 1970-01-01
        • 2022-12-09
        • 2021-06-04
        • 2021-12-24
        相关资源
        最近更新 更多