【问题标题】:Optimization: Splitting a multidimensional array into subarrays优化:将多维数组拆分为子数组
【发布时间】:2018-08-05 07:26:04
【问题描述】:

我有一个由以下格式组成的多维数组:

var source = [['Item 1', 10],['Item 2', 15],['Item 3', 50],['Item 4', 25]];

这由两个并行数据集组成:source[x][0] 是数据,source[x][1] 是应用于每个数据项的权重。我发现自己需要将其拆分为组件数组、数据和权重。我可以(并且目前)通过以下方式执行此操作:

var weighting = [], data = [];
for (var index = 0; index < source.length; index++)
{
    data.push(source[index][0]);
    weighting.push(source[index][1]);
}

这导致我有以下两个数组(继续前面的示例):

var data = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
var weighting = [10, 15, 50, 25];

有没有更快的方法来做到这一点?我正在使用的源数组包含大约 200 万组数据和权重,因此与一次将这些分开一个条目的循环相比,成本更低的方法可以显着节省 CPU 时间。

【问题讨论】:

  • 提示:这被称为转置一个二维数组。查找该术语可能会导致更好的解决方案
  • 除了访问source[index] 一次之外,没有什么可以让这更快。您要求的操作本质上需要O(n) 时间。如果您想要实际节省,您将需要优化您的整个算法。告诉我们为什么你在 JavaScript 中有这么大的数组,以及你打算用它们做什么。
  • 也许这没有帮助,因为您可能无法进行更改,但为什么不首先避免创建多维数组,例如创建一个数组,其中source[i] 是一个基准,source[source.length+i] 是它的权重?然后,您可以将数组分成相等的两半以获得您想要的:source.slice(0, source.length/2)source.slice(source.length/2, source.length)

标签: javascript arrays optimization multidimensional-array


【解决方案1】:

这是一个代码审查问题,因为此解决方案可以正常运行。您最感兴趣的是性能。

现在,for 循环通常会胜过任何其他解决方案。但是,您可以稍微提高性能。

  • 在循环中缓存数组的长度变量,避免不断检查长度。
  • 在循环中检索索引对,而不是引用它两次。

这是一个例子:

var source = [
  ['Item 1', 10],
  ['Item 2', 15],
  ['Item 3', 50],
  ['Item 4', 25]
];

var data = [],
  weightings = [];

for (var i = 0, l = source.length; i < l; i++) {
  var item = source[i];
  data.push(item[0]);
  weightings.push(item[1])
}

console.log(data);
console.log(weightings);

根据for 循环中的示例,我们将l 的值分配给数组的长度。现在,var i = 0, l = source.length; 在循环期间有效地缓存了它。

接下来,我们在循环 var item = source[i]; 中检索索引对(数组),因此我们不必在数组中定位它两次。

这可以在jsPerf: Reduce vs. Loop 12 上测试。

现在实际上有 2,000,000 条记录,原始方法不需要太多时间(在我的测试机器上大约需要 100 毫秒)。

这是一个测试示例,检查控制台时间非常接近。

JSFiddle

绩效考核

机器(i7 5820k 64 GB DDR4 RAM): 请注意,出于兴趣,我已经更新了小提琴以包含 while 版本。

Old Version took 83.56999999999994 ms
Perf Version took 44.24500000000012 ms
While took 76.2800000000002 ms

对我来说,这是一个 ~45% 的增长,是的,这只是时间上的微小差异。

【讨论】:

  • 我明白你的意思,这里的优化是 37ms 和 43.5ms 之间的差异(在我的笔记本电脑上使用你的 jsfiddle)。这是相当微不足道的(尽管 15% 的改进也不容小觑),即使使用我正在使用的数据集类,但取决于数据的处理方式(如果此代码用于多个大小相当但唯一的代码)数据集)它可以加起来。
【解决方案2】:

此函数只查看数组中的每个项目并将各个属性推送到新数组中。底部的两个循环不是必需的。我把它们放在那里查看结果。

var source = [['Item 1', 10],['Item 2', 15],['Item 3', 50],['Item 4', 25]];
var arr1 = [];
var arr2 = [];

splitArray();

function splitArray() {
  for (var i = 0; i < source.length; i++) {
    var item = source[i];
    arr1.push(item[0]);
    arr2.push(item[1]);
  }

  for(var j = 0; j < arr1.length; j++) {
    console.log(arr1[j]);
  }

  for(var k = 0; k < arr1.length; k++) {
    console.log(arr2[k]);
  }
}

【讨论】:

  • 这是在 Nico 的一半答案上放置一个函数包装器。将 source.length 的缓存添加到 splitArray() 函数并删除“结果查看”循环将改善这个答案。此外,如果要使其更通用(将其变为函数而不是代码 sn-p),则将 source 作为参数传递并返回两个数组可能很有用......尽管将两个数组作为一个返回参数与函数的意图背道而驰....将二维数组拆分为其组件以进行单独处理。
  • 考虑到他的答案在我发布答案后得到了严重更新,我并不真正担心重叠。我在回答中说可以删除循环。这是解决问题的想法,而不是完整的解决方案。根据问题陈述和尝试的解决方案,您甚至不知道该怎么做。它仅用作提示。
  • 我会修改我的评论。我什至没有看到他的回答。当我开始写作时,那里还有其他一些完全不同的答案。我的和他的答案几乎是一样的。但是,我没有复制他的答案。
  • @ChrisSharp 我的原始答案比您早 12 分钟,我的更新包括与原因和性能基准测试相关的其他信息。 stackoverflow.com/revisions/42684346/1
  • 如果我知道执行此操作的最佳方法,我会回答我自己的主题问答风格,以此将答案传播给其他人。对我进行人身攻击无济于事。我只是评论说,这里已有的较旧答案在回答问题方面做得更好,同时提出了建设性的方法来改进您自己的答案。如果这被视为对您的人身攻击,我深表歉意,因为这不是我的意图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-19
  • 2023-03-13
相关资源
最近更新 更多