如何在javascript中避免for循环for循环

时间:2018-01-23 22:04:00

标签: javascript for-loop foreach

我写了一段运行正常的代码。我想要一个新的数组,该数组由来自myArr的元素按orderArr中指定的顺序组成。但是,它在另一个for循环中使用for循环来匹配数组元素。

var myArr = ['a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c'];
var reArr = [];

for (var i = 0; i < orderArr.length; i++) {
  for (var k = 0; k < myArr.length; k++) {
    if (myArr[k] == orderArr[i]) {
      reArr.push(myArr[k]);
    }
  }
}

console.log(reArr);

我经常听说在另一个for循环中使用for循环是不好的做法,即使每个都应该避免。

我怎样才能重新编写此代码。

9 个答案:

答案 0 :(得分:4)

在您的案例中嵌套循环的复杂性为O(n * m) - n为orderArr的长度,m为myArr的长度。

此解决方案复杂度为O(n + m),因为我们使用复杂度为O(m)的Array#reduce创建字典对象,然后使用filtering orderArray创建字典对象O(n)的复杂性。

注1:对于小型数组,这应该不重要。因此,除非你在两个数组中都有几千个,否则你可以继续使用嵌套循环。

注2:此代码假定myArr中没有重复项。如果存在重复项,则结果将与嵌套循环的结果不同。

var myArr = ['a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c'];
var myArrDict = myArr.reduce(function(r, s) {
  r[s] = true;
  return r;
}, Object.create(null));

var reArr = orderArr.filter(function(s) {
  return this[s];
}, myArrDict);

console.log(reArr);

答案 1 :(得分:4)

我不一定会说在循环中使用循环是一种不好的做法 - 实际上Ori Drori打败了我,说明这种做法的效率可能只取决于数据集的大小。

例如,sorting algorithms有很多实现,你经常会在循环中找到循环。但是,随着数据大小的变化,实施细节会影响性能。

我个人的经验是,当我看到嵌套循环时,我立即问自己,它所执行的操作是否需要使用不同的算法进行优化。通常情况下,在JavaScript(浏览器)应用程序中,答案是&#34; no&#34;因为数据大小很小,不足以产生有影响力的差异。

答案 2 :(得分:1)

就个人而言,我喜欢为此特定场景设置专用的关联功能。

"use strict";

function correlate(
  outer,
  inner, 
  outerKeyOrKeySelector = x => x,
  innerKeyOrKeySelector = x => x
) {
  const outerValues = [...outer];
  const innerValues = [...inner];
  const outerToKey = typeof outerKeyOrKeySelector === 'function'
      ? outerKeyOrKeySelector
      : (x => x[outerKeyOrKeySelector]);

  const innerToKey = typeof innerKeyOrKeySelector === 'function'
      ? innerKeyOrKeySelector
      : (x => x[innerKeyOrKeySelector]);

  const outerKeyed = outerValues.map(value => ({key: outerToKey(value), value});
  const innerKeyed = innerValues.map(value => ({key: innerToKey(value), value});

  return outerKeyed.reduce((results, {key: oKey, value: oValue}) => [
     ...results,
     ...innerKeyed
         .filter(({key: iKey}) => oKey === iKey)
         .map(({value: iValue}) => [oValue, iValue])
    ], []);
}

这基本上就像一个JOIN,允许你根据属性值或任意投影函数关联两个数组。

看起来有点矫枉过正,而YMMV,但我发现它非常有用。

应用于手头的问题:

reArr = correlate(orderArr, myArr).map(([first, second]) => first);

但它可以轻松处理复杂的场景

reArr = correlate(orders, customers, o => o.customer.name, c => c.name)
  .map(([first, second]) => {order: first, customer: second})
  .forEach(({customer, order}) => {
    customer.orders = [...customer.orders, order];
  });

答案 3 :(得分:1)

为了补充这里的一些好答案(尤其是Ori&#39; s),我认为两个非常大的数组的最佳解决方案是合并连接(这是关系数据库处理案例的方式。这里的想法我们只是在比较我们所知道的大致相同的值。

第一步是对两个数组(O(n log n)+ O(m log m))进行排序,然后比较每个数组中的第一个值。有三种可能性:

LinkGeneratorInterface

...并重复这个过程,直到其中一个列表耗尽...

A<B
  We discard A and fetch the next value from the same list A came from
A=B
  We add A to the keep list and retrieve the next value from that array
A>B
  We discard B and retrieve the next value from its list

答案 4 :(得分:0)

我认为您的代码没有任何问题,但如果您真的想要,可以使用此功能消除(对您可见)循环:

var reArr = myArr.filter((n) => orderArr.includes(n));

(在旧版本的IE中不起作用)。

答案 5 :(得分:0)

这是一个非常简单的ES5版本 - 如果您的值不是对象,则内部循环避免: -

var myArr = ['a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c'];
var myArrIndex = {};

for(var i=0;i < myArr .length; i++){
    myArrIndex[myArr[i]]=myArr[i];
}; 

for(var i=0;i < orderArr.length; i++){
        var orderArrayItem = orderArr[i];
        if (myArrIndex[orderArrayItem]){
            reArr.push(orderArrayItem);
        }
}


console.log(reArr);

答案 6 :(得分:0)

myArr映射到对象中。我会添加一个&#39; a&#39;到myArr和&#39; f&#39;到orderArr

var myArr = ['a', 'a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c', 'f']
var myObj = createObjectFrom(myArr);
var reArr = [];

function createObjectFrom(array) {
   var obj = {};
   var arrValue;

   for (var i = 0; i < array.length; i++) {
     arrValue = array[i];

     if (obj[arrValue]) {
       obj[arrValue]++;
     } else {
       obj[arrValue] = 1;
     }
   }

   return obj;  // { 'a': 2, 'b': 1, 'c': 1, 'd': 1, 'e': 1 }
}

var orderValue;

for(var i=0;i<orderArr.length;i++){
  orderValue = orderArr[i];

  if (myObj.hasOwnProperty(orderValue)) {
     for (var j = 0; j < myObj[orderValue]; j++) {
       reArr.push(orderValue);
     }
   }
}

console.log(reArr);  // [ "c", "e" ]

而不是循环(6 * 3 =)18次,你只需要循环(6 + 3 =)9次。

我考虑到重复项使代码更复杂一些,因此第二个用于循环(j)。

作为一个额外的奖励,这是一个我觉得有趣的视频:

https://www.youtube.com/watch?v=XKu_SEDAykw

答案 7 :(得分:0)

你可以做的是组合两个数组并使用reduce删除除重复项之外的所有内容,如下所示:

var myArr = ['a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c'];
// Merge the two arrays
var reArr = myArr.concat(orderArr);

let result = reArr.reduce((arr, val) => {
  // return only equal values that are not already in the new array
  // If the value is in the new array 'indexOf()' will be greater than -1
  // we will then not modify the current array
  //
  // If the value is not in the new array and is equal to the current lookup 
  // then add it to the filter. Once filtered, we can check
  // if the filter contains more than one item if it does
  // then we can add it to the new array
  return reArr.filter(v => v == val && arr.indexOf(val) == -1).length > 1 
    // If it is not in the new array add it using 'concat()'
    // otherwise we will return the current array as is
    ? arr.concat(val) : arr
}, [])

console.log(result);

答案 8 :(得分:0)

您可以使用Map并在数组中收集所有相同的值,以便稍后与结果集进行连接。

&#13;
&#13;
var array = ['a', 'b', 'c', 'd', 'e'],
    order = ['e', 'c'],
    map = new Map,
    result;
    
array.forEach(v => map.set(v, (map.get(v) || []).concat(v)));

result = order.reduce((r, v) => r.concat(map.get(v)), []);

console.log(result);
&#13;
&#13;
&#13;

相关问题