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

Javascript - recursion/for loop for flattening array

本文关键字:递归 for 循环 数组 用于 Javascript-      更新时间:2023-09-26

因此,这里有一个解决数组扁平化问题的示例解决方案。我的问题不是"如何"使数组变平。相反,我试图理解这种递归中出现的一些底层功能。

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

我的问题是,"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]]]]);

我认为会有更好的答案,但现在。。。

至少,不要把它看作是"打破"循环,而是把它看作继续按过程顺序执行代码。因此,在循环的上下文中,它将自己称为一个函数,当该函数完成后,它将继续循环。示例

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.

所以重点是,它只是执行一个步骤的过程。它不必"记住"它在哪里,当函数返回时,javascript只需从调用函数的位置继续处理。

您可能希望查看调用堆栈。

结果数组flatArray位于辅助函数的词法闭包中,因此本身不是数组的元素被推到这个位置,并且它们在结果上的放置顺序与它们的迭代顺序相同。

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

想象一下这个功能:

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

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

它实际上并没有通过只发生一次的函数将元素"放回"。它检查一个元素是否是数组,然后在该数组中循环。

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

让我们举以下例子:[1, [2], [3, [[4]]]]

我们开始循环->

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

每个递归步骤的"保持跟踪"与任何其他方法调用的工作方式相同;javascript引擎在调用堆栈中调用新方法时随时跟踪变量和作用域。

对于您的特定示例,也许当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]

由于flatten函数不再可访问flatArray变量,因此需要进行一些细微的修改。但是MOD让steamrollArray看起来是多余的。。。让我们摆脱它。

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]

现在,递归发生的位置和方式更加明显了。每当一个数组被"发现"时,它也会被压扁。值被推送到一个新数组中,或者被推送到作为第二个参数传递的数组中。每次在for循环内调用flatten时,位置i处的阵列本身被展平并推到flatArray上。由于flatArray也被递归地传递给flatten函数,因此所有值都将被收集到该数组中。

我有一个更简单的解决方案,可以处理数组中的任何级别的嵌套。

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;
}

使用for-of循环和2个其他方式压平阵列的解决方案

用于循环

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);

let array = [1, [2], [3, [[4]]]];
const final = [];
const stack = [];
for (let i = 0; i < array.length; i++) {
  const ele = array[i];
  stack.push(ele);
  
  while (stack.length) {
    const first = stack.shift();
    if (Array.isArray(first)) {
      first.forEach(ele => stack.push(ele))
    } else {
      final.push(first)
    }
  }
}
console.log( final.join(', '));