【问题标题】:Map Reduce across javascript array of objects with poor performance跨性能较差的javascript对象数组映射Reduce
【发布时间】:2017-05-10 19:26:35
【问题描述】:

我正在尝试对一些 javascript 对象进行一些映射/减少,但失败得很惨。

来自后端的数据看起来像这样:

[
  {table:"a1", data: {colA:1,colB:2,colC:3}},
  {table:"a2", data: {colA:2,colB:3,colC:4}},
  {table:"a3", data: {colA:3,colB:4,colC:5}}
]

Recharts 需要以下格式的数据(将源数据键作为结果的唯一“名称”键)

[
  {name: 'colA', a1: 1, a2: 2, a3: 3},
  {name: 'colB', a1: 2, a2: 3, a3: 4},
  {name: 'colC', a1: 3, a2: 4, a3: 5}
]

我目前的解决方案是O(n^n),因为我正在构建一个结果对象,并且每次都循环它。我正在使用 ECMA6/Babel 以及 Lodash。任何指导将不胜感激!谢谢!

编辑:这是我目前的解决方案

var dest = []
Lodash.forEach(source,(o) => {
  var table = o.table;
  Lodash.forEach(o.data, (p,q) => {
    // See if the element is in the array
    const index = Lodash.findIndex(dest,(a) => {return a.name === q});
    if ( index === -1) {
      var obj = {};
      obj[table] = Number(p);
      obj.name = q;
      dest.push(obj);
    } else {
      dest[index][table] = Number(p);
    }
  })
});

【问题讨论】:

  • 具体的问题是什么?如果您需要性能,请使用原始for
  • 您能发布您当前的解决方案吗?
  • 请向我们展示您当前的代码。
  • 您的解决方案具有复杂性 O(n² m),而不是 O(n^n)

标签: javascript ecmascript-6 lodash


【解决方案1】:

首先,您的算法的速度实际上是 O(n^n^n),因为您嵌套了 3 个循环。我简化它的方法是首先使用一个对象,然后从该对象创建一个数组。像这样:

function convert(original) {
    var tmp = {};
    for(var tableIndex in original) {
        var tableObj = original[tableIndex];
        for(var colKey in tableObj.data) {
            var col = tableObj.data[colKey];
            if(tmp[colKey] === undefined) {
                tmp[colKey] = {name: colKey};
            }
            tmp[colKey][tableObj.table] = col
        }
    }

    var output = [];
    for(var index in tmp) {
        output.push(tmp[index]);
    }

    return output;
}

var original = [
  {table:"a1", data: {colA:1,colB:2,colC:3}},
  {table:"a2", data: {colA:2,colB:3,colC:4}},
  {table:"a3", data: {colA:3,colB:4,colC:5}}
]

console.log(convert(original));

这将消除循环结果数组以添加到对象的需要。在迭代输入数组及其数据时,您仍然有 O(n^m) + O(l) 条件,但是通过在每次迭代时也迭代结果数组,您没有更复杂的速度条件.

此函数还可以处理每个表的数据可能不同的情况。因此,例如,您可能在其中一个条目中有 colA 和 colB 但没有 colC。或者你可能对另一个条目有感冒。

【讨论】:

    【解决方案2】:

    如果您使用地图来跟踪最终的列,则可以大大简化此操作。通过随时将它们存储在地图中,您可以获得持续查找时间的好处。

    如果我们说表数是N,列数是M,那么你会得到O(N*M)。

    let input = [
      { table: "a1", data: { colA: 1, colB: 2, colC: 3 } },
      { table: "a2", data: { colA: 2, colB: 3, colC: 4 } },
      { table: "a3", data: { colA: 3, colB: 4, colC: 5 } }
    ];
    
    let desiredOutput = [
      { name: 'colA', a1: 1, a2: 2, a3: 3 },
      { name: 'colB', a1: 2, a2: 3, a3: 4 },
      { name: 'colC', a1: 3, a2: 4, a3: 5 }
    ];
    
    let keys = null;
    let map = null;
    
    input.forEach(row => {
      if (map === null) {
        // Cache the column names
        keys = Object.keys(row.data);
        
        // Generates objects such a `{ name: 'colA' }`
        // and stores them at a key of 'colA'
        map = keys
          .reduce((o, k) => (o[k] = {
            name: k
          }, o), {});
      }
    
      // For each column ('colA', 'colB', etc.)
      keys.forEach(key => {
        // Create a new property for the table name
        // ('a1', 'a2', etc.)
        // and copy the matching column value from the input
        map[key][row.table] = row.data[key];
      });
    });
    
    // Convert the map to an array of just the values
    let output = Object.values(map);
    console.log(JSON.stringify(output) === JSON.stringify(desiredOutput));
    console.log(output);

    【讨论】:

    • 很好地使用了一些辅助方法,例如 reduce 和 forEach。请注意,它们也会产生自己的开销。我对我们的两个解决方案进行了速度测试,您的解决方案在 1,000,000 次迭代中的时钟频率约为 1500 毫秒,而对于相同数量的迭代,我的时钟频率约为 900 毫秒。我们之间的唯一区别是您使用辅助方法,而我使用更多 vanilla javascript。请注意方便。
    • @PeterLaBanca 非常正确。如果数据集非常大并且绝对需要速度,则应避免使用辅助方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 2023-03-18
    • 2016-01-01
    • 2021-01-30
    • 1970-01-01
    • 2020-09-14
    相关资源
    最近更新 更多