简体   繁体   English

Javascript递归数组展平

[英]Javascript recursive array flattening

I'm exercising and trying to write a recursive array flattening function.我正在练习并尝试编写一个递归数组展平函数。 The code goes here:代码在这里:

function flatten() {
    var flat = [];
    for (var i = 0; i < arguments.length; i++) {
        if (arguments[i] instanceof Array) {
            flat.push(flatten(arguments[i]));
        }
        flat.push(arguments[i]);
    }
    return flat;
}

The problem is that if I pass there an array or nested arrays I get the "maximum call stack size exceeded" error.问题是,如果我向那里传递一个数组或嵌套数组,我会收到“超出最大调用堆栈大小”错误。 What am I doing wrong?我究竟做错了什么?

The problem is how you are passing the processing of array, if the value is an array then you are keep calling it causing an infinite loop问题是你如何传递数组的处理,如果值是一个数组,那么你一直在调用它导致一个无限循环

function flatten() {
    var flat = [];
    for (var i = 0; i < arguments.length; i++) {
        if (arguments[i] instanceof Array) {
            flat.push.apply(flat, flatten.apply(this, arguments[i]));
        } else {
            flat.push(arguments[i]);
        }
    }
    return flat;
}

Demo: Fiddle演示:小提琴

Here's a more modern version:这是一个更现代的版本:

function flatten(items) {
  const flat = [];

  items.forEach(item => {
    if (Array.isArray(item)) {
      flat.push(...flatten(item));
    } else {
      flat.push(item);
    }
  });

  return flat;
}

The clean way to flatten an Array in 2019 with ES6 is flat()2019 年使用ES6扁平化数组的干净方法是flat()

Short Answer:简短的回答:

array.flat(Infinity)

Detailed Answer:详细答案:

const array = [1, 1, [2, 2], [[3, [4], 3], 2]]

// All layers
array.flat(Infinity) // [1, 1, 2, 2, 3, 4, 3, 2]

// Varying depths
array.flat() // [1, 1, 2, 2, Array(3), 2]

array.flat(2) // [1, 1, 2, 2, 3, Array(1), 3, 2]
array.flat().flat() // [1, 1, 2, 2, 3, Array(1), 3, 2]

array.flat(3) // [1, 1, 2, 2, 3, 4, 3, 2]
array.flat().flat().flat() // [1, 1, 2, 2, 3, 4, 3, 2]

Mozilla Docs Mozilla 文档

Can I Use - 95% Jul '22我可以使用- 95% 22 年 7 月

If the item is array, we simply add all the remaining items to this array如果项目是数组,我们只需将所有剩余的项目添加到这个数组中

 function flatten(array, result) { if (array.length === 0) { return result } var head = array[0] var rest = array.slice(1) if (Array.isArray(head)) { return flatten(head.concat(rest), result) } result.push(head) return flatten(rest, result) } console.log(flatten([], [])) console.log(flatten([1], [])) console.log(flatten([1,2,3], [])) console.log(flatten([1,2,[3,4]], [])) console.log(flatten([1,2,[3,[4,5,6]]], [])) console.log(flatten([[1,2,3],[4,5,6]], [])) console.log(flatten([[1,2,3],[[4,5],6,7]], [])) console.log(flatten([[1,2,3],[[4,5],6,[7,8,9]]], []))

[...arr.toString().split(",")]

Use the toString() method of the Object .使用ObjecttoString()方法。 Use a spread operator (...) to make an array of string and split it by "," .使用扩展运算符(...)创建一个字符串数组并用","分割它。

Example:例子:

let arr =[["1","2"],[[[3]]]]; // output : ["1", "2", "3"]

A Haskellesque approach...哈斯克尔式的方法...

 function flatArray([x,...xs]){ return x !== undefined ? [...Array.isArray(x) ? flatArray(x) : [x],...flatArray(xs)] : []; } var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10], fa = flatArray(na); console.log(fa);

So i think the above code snippet could be made easier to understand with proper indenting;所以我认为上面的代码片段可以通过适当的缩进更容易理解;

function flatArray([x,...xs]){
  return x !== undefined ? [ ...Array.isArray(x) ? flatArray(x)
                                                 : [x]
                           , ...flatArray(xs)
                           ]
                         : [];
}

var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10],
    fa = flatArray(na);
console.log(fa);

If you assume your first argument is an array, you can make this pretty simple.如果你假设你的第一个参数是一个数组,你可以让它变得非常简单。

function flatten(a) {
    return a.reduce((flat, i) => {
      if (Array.isArray(i)) {
        return flat.concat(flatten(i));
      }
      return flat.concat(i);
    }, []);
  }

If you did want to flatten multiple arrays just concat them before passing.如果您确实想展平多个数组,只需在通过之前将它们连接起来。

If someone looking for flatten array of objects (eg tree ) so here is a code:如果有人正在寻找扁平化的对象数组(例如tree ),那么这里是一个代码:

 function flatten(items) { const flat = []; items.forEach(item => { flat.push(item) if (Array.isArray(item.children) && item.children.length > 0) { flat.push(...flatten(item.children)); delete item.children } delete item.children }); return flat; } var test = [ {children: [ {children: [], title: '2'} ], title: '1'}, {children: [ {children: [], title: '4'}, {children: [], title: '5'} ], title: '3'} ] console.log(flatten(test))

Your code is missing an else statement and the recursive call is incorrect (you pass the same array over and over instead of passing its items).您的代码缺少 else 语句并且递归调用不正确(您一遍又一遍地传递相同的数组,而不是传递其项目)。

Your function could be written like this:你的函数可以这样写:

function flatten() {
    // variable number of arguments, each argument could be:
    // - array
    //   array items are passed to flatten function as arguments and result is appended to flat array
    // - anything else
    //   pushed to the flat array as-is
    var flat = [],
        i;
    for (i = 0; i < arguments.length; i++) {
        if (arguments[i] instanceof Array) {
            flat = flat.concat(flatten.apply(null, arguments[i]));
        } else {
            flat.push(arguments[i]);
        }
    }
    return flat;
}

// flatten([[[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]], [[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]]]);
//            [0, 1, 2,   0, 1, 2,     0, 1, 2,   0, 1, 2,       0, 1, 2,   0, 1, 2,     0, 1, 2,   0, 1, 2]

Modern but not crossbrowser现代但不是跨浏览器

function flatten(arr) {
  return arr.flatMap(el => {
    if(Array.isArray(el)) {
        return flatten(el);
    } else {
      return el;
    }
  });
}

This is a Vanilla JavaScript solution to this problem这是针对此问题的 Vanilla JavaScript 解决方案

 var _items = {'keyOne': 'valueOne', 'keyTwo': 'valueTwo', 'keyThree': ['valueTree', {'keyFour': ['valueFour', 'valueFive']}]}; // another example // _items = ['valueOne', 'valueTwo', {'keyThree': ['valueTree', {'keyFour': ['valueFour', 'valueFive']}]}]; // another example /*_items = {"data": [{ "rating": "0", "title": "The Killing Kind", "author": "John Connolly", "type": "Book", "asin": "0340771224", "tags": "", "review": "i still haven't had time to read this one..." }, { "rating": "0", "title": "The Third Secret", "author": "Steve Berry", "type": "Book", "asin": "0340899263", "tags": "", "review": "need to find time to read this book" }]};*/ function flatten() { var results = [], arrayFlatten; arrayFlatten = function arrayFlattenClosure(items) { var key; for (key in items) { if ('object' === typeof items[key]) { arrayFlatten(items[key]); } else { results.push(items[key]); } } }; arrayFlatten(_items); return results; } console.log(flatten());

Here's a recursive reduce implementation taken from absurdum that mimics lodash's _.concat()这是一个从模仿 lodash 的_.concat()荒谬中提取的递归减少实现

It can take any number of array or non-array arguments.它可以采用任意数量的数组或非数组参数。 The arrays can be any level of depth.阵列可以是任何深度级别。 The resulting output will be a single array of flattened values.结果输出将是单个扁平值数组。

export const concat = (...arrays) => {
  return flatten(arrays, []);
}

function flatten(array, initial = []) {
  return array.reduce((acc, curr) => {
    if(Array.isArray(curr)) {
      acc = flatten(curr, acc);
    } else {
      acc.push(curr);
    }
    return acc;
  }, initial);
}

It can take any number of arrays or non-array values as input.它可以将任意数量的数组或非数组值作为输入。

Source: I'm the author of absurdum来源:我是荒谬的作者

Here you are my functional approach:这是我的功能方法:

const deepFlatten = (array => (array, start = []) => array.reduce((acc, curr) => {
    return Array.isArray(curr) ? deepFlatten(curr, acc) : [...acc, curr];
}, start))();

console.log(deepFlatten([[1,2,[3, 4, [5, [6]]]],7]));

A recursive approach to flatten an array in JavaScript is as follows.在 JavaScript 中展平数组的递归方法如下。

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

let array = [[1, 2, 3], [[4, 5], 6, [7, 8, 9]]];
console.log(flatten(array));    
// Output = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

let array2 = [1, 2, [3, [4, 5, 6]]];
console.log(flatten(array2));    
// Output = [ 1, 2, 3, 4, 5, 6 ]

The function below flat the array and mantains the type of every item not changing them to a string.下面的函数将数组变平并保持每个项目的类型,而不是将它们更改为字符串。 It is usefull if you need to flat arrays that not contains only numbers like items.如果您需要不只包含数字之类的数字的平面数组,这很有用。 It flat any kind of array with free of side effect.它可以平整任何类型的阵列,没有副作用。

function flatten(arr) {
  for (let i = 0; i < arr.length; i++) {
    arr = arr.reduce((a, b) => a.concat(b),[])
  }
  return arr
}

console.log(flatten([1, 2, [3, [[4]]]]));
console.log(flatten([[], {}, ['A', [[4]]]]));

Another answer in the list of answers, flattening an array with recursion:答案列表中的另一个答案,使用递归展平数组:

 let arr = [1, 2, [3, 4, 5, [6, 7, [[8], 9, [10]], [11, 13]], 15], [16, [17]]]; let newArr = []; function steamRollAnArray(list) { for (let i = 0; i < list.length; i++) { if (Array.isArray(list[i])) { steamRollAnArray(list[i]); } else { newArr.push(list[i]); } } } steamRollAnArray(arr); console.log(newArr);

To simplify, check whether the element at an index is an array itself and if so, pass it to the same function.为简化起见,检查索引处的元素是否是数组本身,如果是,则将其传递给同一函数。 If its not an array, push it to the new array.如果它不是数组,则将其推送到新数组。

This should work这应该工作

function flatten() {
    var flat = [
    ];
    for (var i = 0; i < arguments.length; i++) {
        flat = flat.concat(arguments[i]);
    }
    var removeIndex = [
    ];
    for (var i = flat.length - 1; i >= 0; i--) {
        if (flat[i] instanceof Array) {
            flat = flat.concat(flatten(flat[i]));
            removeIndex.push(i);
        }
    }
    for (var i = 0; i < removeIndex.length; i++) {
        flat.splice(removeIndex - i, 1);
    }
    return flat;
}

The other answers already did point to the source of the OP's code malfunction.其他答案确实指出了 OP 代码故障的根源。 Writing more descriptive code, the problem literally boils down to an "array-detection/-reduce/-concat-recursion" ...编写更具描述性的代码,问题实际上归结为“数组检测/-reduce/-concat-recursion”......

(function (Array, Object) {


//"use strict";


  var
    array_prototype       = Array.prototype,

    array_prototype_slice = array_prototype.slice,
    expose_internal_class = Object.prototype.toString,


    isArguments = function (type) {
      return !!type && (/^\[object\s+Arguments\]$/).test(expose_internal_class.call(type));
    },
    isArray     = function (type) {
      return !!type && (/^\[object\s+Array\]$/).test(expose_internal_class.call(type));
    },

    array_from  = ((typeof Array.from == "function") && Array.from) || function (listAlike) {
      return array_prototype_slice.call(listAlike);
    },


    array_flatten = function flatten (list) {
      list = (isArguments(list) && array_from(list)) || list;

      if (isArray(list)) {
        list = list.reduce(function (collector, elm) {

          return collector.concat(flatten(elm));

        }, []);
      }
      return list;
    }
  ;


  array_prototype.flatten = function () {
    return array_flatten(this);
  };


}(Array, Object));

borrowing code from one of the other answers as proof of concept ...从其他答案之一借用代码作为概念证明......

console.log([
  [[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]],
  [[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]]
].flatten());
//[0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, ..., ..., ..., 0, 1, 2]

I hope you got all kind of different.我希望你能有所不同。 One with a combination of recursive and "for loop"/high-order function.一种结合了递归和“for循环”/高阶函数。 I wanted to answer without for loop or high order function.我想在没有 for 循环或高阶函数的情况下回答。

Check the first element of the array is an array again.再次检查数组的第一个元素是一个数组。 If yes, do recursive till you reach the inner-most array.如果是,请执行递归,直到到达最里面的数组。 Then push it to the result.然后推到结果。 I hope I approached it in a pure recursive way.我希望我以纯粹的递归方式接近它。

function flatten(arr, result = []) {
  if(!arr.length) return result;
  (Array.isArray(arr[0])) ? flatten(arr[0], result): result.push(arr[0]);
  return flatten(arr.slice(1),result)
}

I think the problem is the way you are using arguments .我认为问题在于您使用arguments的方式。

since you said when you pass a nested array, it causes "maximum call stack size exceeded" Error.因为你说当你传递一个嵌套数组时,它会导致“超出最大调用堆栈大小”错误。

because arguments[0] is a reference pointed to the first param you passed to the flatten function.因为arguments[0]是指向您传递给flatten函数的第一个参数的引用。 for example:例如:

   flatten([1,[2,[3]]]) // arguments[0] will always represents `[1,[2,[3]]]`

so, you code ends up calling flatten with the same param again and again.因此,您的代码最终会一次又一次地使用相同的参数调用flatten

to solve this problem, i think it's better to use named arguments , rather than using arguments , which essentially not a "real array".为了解决这个问题,我认为最好使用named arguments ,而不是使用arguments ,它本质上不是“真正的数组”。

There are few ways to do this:有几种方法可以做到这一点:

  1. using the flat method and Infinity keyword:使用 flat 方法和 Infinity 关键字:

    const flattened = arr.flat(Infinity);

  2. You can flatten any array using the methods reduce and concat like this:您可以使用 reduce 和 concat 方法展平任何数组,如下所示:

    function flatten(arr) { return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flatten(cur) : cur), []); };

Read more at: https://www.techiedelight.com/recursively-flatten-nested-array-javascript/阅读更多: https ://www.techiedelight.com/recursively-flatten-nested-array-javascript/

const nums = [1,2,[3,4,[5]]];
const chars = ['a',['b','c',['d',['e','f']]]];
const mixed = ['a',[3,6],'c',[1,5,['b',[2,'e']]]];  

const flatten = (arr,res=[]) => res.concat(...arr.map((el) => (Array.isArray(el)) ? flatten(el) : el));

console.log(flatten(nums)); // [ 1, 2, 3, 4, 5 ]
console.log(flatten(chars)); // [ 'a', 'b', 'c', 'd', 'e', 'f' ]
console.log(flatten(mixed)); // [ 'a', 3, 6, 'c', 1, 5, 'b', 2, 'e' ]

Here is the breakdown:这是细分:

  1. loop over "arr" with "map"用“map”循环“arr”

arr.map((el) => ...) arr.map((el) => ...)

  1. on each iteration we'll use a ternary to check whether each "el" is an array or not在每次迭代中,我们将使用一个三元来检查每个“el”是否是一个数组

(Array.isArray(el)) (Array.isArray(el))

  1. if "el" is an array, then invoke "flatten" recursively and pass in "el" as its argument如果 "el" 是一个数组,则递归调用 "flatten" 并传入 "el" 作为其参数

flatten(el)展平(el)

  1. if "el" is not an array, then simply return "el"如果 "el" 不是数组,则简单地返回 "el"

: el : 埃尔

  1. lastly, concatenate the outcome of the ternary with "res"最后,将三元的结果与“res”连接起来

res.concat(...arr.map((el) => (Array.isArray(el)) ? flatten(el) : el)); res.concat(...arr.map((el) => (Array.isArray(el)) ? flatten(el) : el));

--> the spread operator will copy all the element(s) instead of the array itself while concatenating with "res" --> 扩展运算符将复制所有元素而不是数组本身,同时与“res”连接

 var nestedArr = [1, 2, 3, [4, 5, [6, 7, [8, [9]]]], 10]; let finalArray = []; const getFlattenArray = (array) => { array.forEach(element => { if (Array.isArray(element)) { getFlattenArray(element) } else { finalArray.push(element) } }); } getFlattenArray(nestedArr);
In the finalArray you will get the flattened array 在 finalArray 中,您将获得展平的数组

Solution using forEach使用 forEach 的解决方案

function flatten(arr) {
  const flat = [];
  arr.forEach((item) => {
    Array.isArray(item) ? flat.push(...flatten(item)) : flat.push(item);
  });
  return flat;  
}

Solution using reduce使用reduce的解决方案

function flatten(arr) {
  return arr.reduce((acc, curr) => {
    if (Array.isArray(curr)) {
      return [...acc, ...flatten(curr)];
    } else {
      return [...acc, curr];
    }
  }, []);
}

I think you are very close.我觉得你很亲近。 One of the problems are that you call the flatten function with the same arguments.问题之一是您使用相同的参数调用 flatten 函数。 We can make use of the spread operator ( ... ) to make sure we are calling flatten on the array inside of arguments[i], and not repeating the same arguments.我们可以使用扩展运算符 ( ... ) 来确保我们在 arguments[i] 内部的数组上调用 flatten,而不是重复相同的参数。

We also need to make a few more adjustments so we're not pushing more items into our array than we should我们还需要做更多的调整,这样我们就不会将更多的项目推入我们的数组中

 function flatten() { var flat = []; for (var i = 0; i < arguments.length; i++) { if (arguments[i] instanceof Array) { flat.push(...flatten(...arguments[i])); } else { flat.push(arguments[i]); } } return flat; } console.log(flatten([1,2,3,[4,5,6,[7,8,9]]],[10,11,12]));

function flatArray(input) {
  if (input[0] === undefined) return [];
  if (Array.isArray(input[0]))
     return [...flatArray(input[0]), ...flatArray(input.slice(1))];
  return [input[0], ...flatArray(input.slice(1))];
}

you should add stop condition for the recursion .您应该为递归添加停止条件。

as an example if len (arguments[i]) ==0 return例如,如果 len (arguments[i]) ==0 返回

我已经在 stackoverflow 的this page中发布了我的数组展平的递归版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM