【问题标题】:How to efficiently build this array?如何有效地构建这个数组?
【发布时间】:2017-04-25 19:49:19
【问题描述】:

我有一个对象数组,例如:

[
  { a: 3, b: 2, c: 5, d: 6, e: 8 },
  { a: 1, b: 5, c: 3, d: 1, e: 2 }
]

现在我想将其转换为仅包含特定属性值但不包含对象本身的数组。例如,如果我对abcd 感兴趣,则结果应如下所示:

[ 3, 2, 5, 6, 1, 5, 3, 1 ]

我目前的做法是这样的:

const result = _.flatten(data.map(item => [ item.a, item.b, item.c, item.d ]));

是否有更好(即更有效,甚至更易读)的方法来获得结果?

【问题讨论】:

  • 您想要/需要使用 lodash/下划线吗?您感兴趣的属性名称列表是静态的还是动态的?

标签: javascript arrays node.js


【解决方案1】:

你所拥有的对我来说似乎很容易阅读,并且可能是有效的足够。但是你可以通过避免所有这些临时数组而不是循环两次来提高效率:

const result = [];
data.forEach(item => result.push(item.a, item.b, item.c, item.d));

例子:

const data = [
  { a: 3, b: 2, c: 5, d: 6, e: 8 },
  { a: 1, b: 5, c: 3, d: 1, e: 2 }
];
const result = [];
data.forEach(item => result.push(item.a, item.b, item.c, item.d));
console.log(result);

有些引擎在push 真的效率很高,而其他引擎则不然。如果效率是一项关键要求,您需要在目标环境中进行实验比较:

const result = [];
let index = 0;
let n, l, item;
for (n = 0, l = data.length; n < l; ++n) {
  item = data[n];
  result[index++] = item.a;
  result[index++] = item.b;
  result[index++] = item.c;
  result[index++] = item.d;
}

这里需要注意三点:

  1. 直接推送到数组而不是使用push
  2. 使用简单的for 循环而不是forEach。使用forEach 的绝对开销非常小,从人类的角度来看几乎不存在,但在紧密循环中它的相对开销却相当大。
  3. for 循环之外声明ilitem。如果我们在for 中声明它们,它们将在每次循环迭代时重新创建,从而增加开销。 (ES2015 的 let 声明语义强大且有用,但在这种特殊情况下,我们不想要开销。)

例子:

const data = [
  { a: 3, b: 2, c: 5, d: 6, e: 8 },
  { a: 1, b: 5, c: 3, d: 1, e: 2 }
];
const result = [];
let index = 0;
let n, l, item;
for (n = 0, l = data.length; n < l; ++n) {
  item = data[n];
  result[index++] = item.a;
  result[index++] = item.b;
  result[index++] = item.c;
  result[index++] = item.d;
}
console.log(result);

【讨论】:

  • @torazaburo:确实是的。昨天下午,当我不在电脑旁时,我发生了这种情况。 :-) 现在完成。
【解决方案2】:

在 ES6 中,不要求效率,您可以编写

const input = [
  { a: 3, b: 2, c: 5, d: 6, e: 8 },
  { a: 1, b: 5, c: 3, d: 1, e: 2 }
];

console.log([].concat(...input.map(({a, b, c, d}) => [a, b, c, d])));

如果你真的对效率感兴趣,没有什么比得上

var result = [];
for (let i = 0; i < input.length; i++) {
  const o = input[i];
  result.push(o.a, o.b, o.c, o.d);
}

尽管正如另一个答案指出的那样,值得将 pushresult[cnt++] = val 进行基准测试。

【讨论】:

    【解决方案3】:

    您可以从数组中的对象中Array.prototype.map() 所需的键,并将它们与Function.prototype.apply()Array.prototype.concat() 合并:

    let data = [{ a: 3, b: 2, c: 5, d: 6, e: 8 }, { a: 1, b: 5, c: 3, d: 1, e: 2 }],
        result = [].concat.apply([], data.map(i => [i.a, i.b, i.c, i.d]));
    
    console.log(result);

    【讨论】:

    • 虽然这是迄今为止最易读的版本恕我直言,但它的主要缺点是 concat 一遍又一遍地创建新数组。还是我错过了什么?
    【解决方案4】:

    您可以使用 2 个嵌套的 forEach 循环来推送输入数组中每个元素的目标值。我在这里建议一个函数接收作为参数的初始数组和作为另一个数组的属性:

    var inflate = (data, options) => {
      var results = [];
      data.forEach(x => options.forEach(y => results.push(x[y])));
      return results;
    }
    
    console.log(inflate([{ a: 3, b: 2, c: 5, d: 6, e: 8 }, { a: 1, b: 5, c: 3, d: 1, e: 2 }] ,["a","b","c","d"]));

    【讨论】:

      【解决方案5】:

      您可以使用映射键来减少。

      var array = [{ a: 3, b: 2, c: 5, d: 6, e: 8 }, { a: 1, b: 5, c: 3, d: 1, e: 2 }],
          keys = ['a', 'b', 'c', 'd'],
          result = array.reduce((r, a) => r.concat(keys.map(k => a[k])), []);
      
      console.log(result);

      【讨论】:

      • 所有应有的尊重,这都是效率较低的可读性。 :-)
      • 但它更灵活。
      • 是的,是的。我只是注意到问题的最后一行。但更灵活可能是“更好”,而不是他提到的“更好”的任何一个例子。 :-)
      • 不使用reduce,而是写成[].concat(...array.map(o =&gt; keys.map(k =&gt; o[k]))) 更为惯用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-16
      • 1970-01-01
      • 2015-07-13
      • 1970-01-01
      • 1970-01-01
      • 2023-02-07
      相关资源
      最近更新 更多