Javascript - 用于展平数组的递归/ for循环

时间:2016-05-08 23:17:47

标签: javascript arrays recursion

因此,这是一个解决扁平化阵列问题的示例解决方案。我的问题不是'如何'平整阵列。相反,我试图理解在这个递归中发生的一些基本功能。

此解决方案遍历原始数组的每个元素,通过将它们放回函数中来分解任何数组元素,直到它们不再是数组并且可以推送到新数组。

我的问题是,' for'循环跟踪元素通过函数返回的所有时间,并继续循环遍历原始'的其余部分。它当时正在处理的数组?它必须以某种方式保持跟踪,否则每次元素是一个数组并被放回时,当前循环将被缩短。希望我的问题有道理。

function steamrollArray(array) {
  var flatArray = [];

  flatten(array);

  function flatten(array) {
    for (var i = 0; i < array.length; i++) {
      if (Array.isArray(array[i])) {
        flatten(array[i]);
      } else {
        flatArray.push(array[i]);
      }
    }
  }

  return flatArray;
}
steamrollArray([1, [2], [3, [[4]]]]);

6 个答案:

答案 0 :(得分:3)

我认为会有更好的答案,但这里有......

至少,不要把它想象成&#34;打破&#34;循环,将其视为继续按程序顺序执行代码。因此,从循环的上下文中,它将自身称为函数,当该函数完成时,它将继续循环。实施例

var item = [1, [2, 3], 4]

flatten(item)的执行将是:

Loop 1: 
  Push 1
Loop 2: 
  Start a call to flatten with [2, 3]
  Loop 1: 
    push 2
  Loop 2: 
    push 3
  End loop
Loop 3: 
  Push 4
End Loop.

所以重点是,它只是执行一个步骤。它不需要记住&#34;它是什么时候,当一个函数返回时,javascript只是继续从调用函数的地方开始处理。

您可以查看call stacks

答案 1 :(得分:2)

结果数组flatArray位于辅助函数的词法闭包中,因此不是自身数组的元素被推送到此,并且它们在结果上的顺序按它们的顺序排列迭代。

当其中一个元素是一个数组时,它调用flatten(array[i]),在返回之前展平该元素的元素。与所有函数一样,语句的执行需要在完成下一个语句之前完成,并且调用函数根本不会取消当前函数。它会恢复并持续到结束或return语句。

想象一下这个功能:

function test () {
  console.log("hello, ");
  console.log("world!");
}

当调用它时,它会等到整个console.log("hello, ")完成后再执行语句二(console.log("world!"))。如果它是递归调用,那么调用需要在执行语句二之前完成。它对所有函数调用都是一样的,不仅仅是递归调用。

答案 2 :(得分:1)

它实际上并没有把元素放回去&#39;通过只发生一次的功能。它检查一个元素是否是一个数组,然后遍历该数组。

如果它不是数组,则将其推送到flatArray。这里的递归是,如果任何元素是一个数组,它将通过flatten函数。然后,如果该数组的元素是一个数组,那么元素将被发送到flatten等等等等。

让我们看一下以下示例:[1, [2], [3, [[4]]]]

我们开始循环 - &gt;

  • index 0是1而不是数组,所以它被推送到flatArray
  • 索引1是一个数组,所以我们递归 - 因为我们有一个2 - 然后被推送
  • 索引2是一个数组,所以我们递归,该内部数组的索引0是非数组3,因此它被推送。然后我们有最后一个索引 - 一个我们进入的数组,找到另一个数组 - 再次递归 - 最后到达4,我们推..

答案 3 :(得分:1)

跟踪&#39;每个递归步骤的发生方式与任何其他方法调用的工作方式相同;只要在call stack中调用新方法,javascript引擎就会跟踪变量和范围。

对于您的具体示例,当<p id="id1"></p> <p id="id2"></p> 函数不再嵌套时,可能会更容易看到发生了什么。

flatten

由于function steamrollArray(array) { var flatArray = []; flatten(array, flatArray); return flatArray; } function flatten(array, flatArray) { flatArray = flatArray || []; for (var i = 0; i < array.length; i++) { if (Array.isArray(array[i])) { flatten(array[i]); } else { flatArray.push(array[i]); } } } steamrollArray([1, [2], [3, [[4]]]]); # [1, 2, 3, 4] 函数无法再访问flatArray变量,因此需要进行一些细微的修改。但是这些mod使得flatten似乎是多余的......让我们摆脱它。

steamrollArray

现在,递归发生的地点和方式更加明显。任何时候发现一个数组&#39;它也会被展平。值可以推送到新数组,也可以推送到作为第二个参数传递的数组中。每次function flatten(array, flatArray) { flatArray = flatArray || []; for (var i = 0; i < array.length; i++) { if (Array.isArray(array[i])) { flatten(array[i], flatArray); } else { flatArray.push(array[i]); } } return flatArray; } flatten([1, [2], [3, [[4]]]]); # [1, 2, 3, 4] 循环内调用flatten时,for位置的数组本身都会展平并推送到i。由于flatArray也以递归方式传递给flatArray函数,所有值都将被收集到该数组中。

答案 4 :(得分:0)

我有一个更简单的解决方案,适用于任何级别的数组嵌套。

function flattenArray(arr){

  for(var i=0;i<arr.length;i++){

    if(arr[i] instanceof Array){

      Array.prototype.splice.apply(arr,[i,1].concat(arr[i]))
       i--;
    }

  }

  return arr;
}

答案 5 :(得分:0)

使用for-of循环和另外两种方法来展平数组的解决方案

使用for-of循环

let array = [1, [2], [3, [[4]]]];
let output = [];
let flattenArray = (arr) => {
  for(let a of arr) {
   Array.isArray(a) ? flattenArray(a) : output.push(a);
  }
  return output;
}
console.log(flattenArray(array));

使用reduce()方法

let array = [1, [2], [3, [[4]]]]
function flattenArray(ary) {
 return ary.reduce((acc, val) => {
  if (Array.isArray(val)) {
   return acc.concat(flattenArray(val))
  }
   return acc.concat(val)
  },[])
}
console.log(flattenArray(array));

使用flat()方法

let array = [1, [2], [3, [[4]]]];
let output = array.flat(Infinity); // use Infinity for flattening nested levels.
console.log(output);