Javascript 如何在javascript中展平嵌套数组?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27266550/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 23:56:56  来源:igfitidea点击:

How to flatten nested array in javascript?

javascriptarrays

提问by zangw

As we know, to flatten the array [[0, 1], [2, 3], [4, 5]]by using the method reduce()

众所周知,要[[0, 1], [2, 3], [4, 5]]使用以下方法展平数组reduce()

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
  return a.concat(b);
});

So how to flatten this array [[[0], [1]], [[2], [3]], [[4], [5]]]to [0, 1, 2, 3, 4, 5]?

因此,如何扁平化这个数组[[[0], [1]], [[2], [3]], [[4], [5]]][0, 1, 2, 3, 4, 5]

回答by Leo

Perfect use case for recursion, which could handle even deeper structure:

递归的完美用例,它可以处理更深的结构:

function flatten(ary) {
    var ret = [];
    for(var i = 0; i < ary.length; i++) {
        if(Array.isArray(ary[i])) {
            ret = ret.concat(flatten(ary[i]));
        } else {
            ret.push(ary[i]);
        }
    }
    return ret;
}

flatten([[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]]) // [0, 1, 2, 3, 4, 5]

Alternatively, as an Array method:

或者,作为 Array 方法:

Array.prototype.flatten = function() {
    var ret = [];
    for(var i = 0; i < this.length; i++) {
        if(Array.isArray(this[i])) {
            ret = ret.concat(this[i].flatten());
        } else {
            ret.push(this[i]);
        }
    }
    return ret;
};

[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flatten() // [0, 1, 2, 3, 4, 5]


EDIT #1: Well, think it a little bit functional way (except for the named recursion which should be using Y-combinator for pure functional :D).

编辑#1:好吧,认为它有点功能性的方式(命名递归除外,它应该使用 Y-combinator 进行纯功能性:D)。

function flatten(ary) {
  return ary.reduce(function(a, b) {
    if (Array.isArray(b)) {
      return a.concat(flatten(b))
    }
    return a.concat(b)
  }, [])
}

Let's adopt some ES6 syntax which makes it even shorter, in one line.

让我们采用一些 ES6 语法,使其更短,在一行中。

const flatten = (ary) => ary.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [])

But remember, this one cannot be applied as an array method, because arrow functionsdon't have theirs own this.

但请记住,这个不能作为数组方法应用,因为箭头函数没有自己的this.



EDIT #2: With the latest Array.prototype.flatproposal this is super easy. The array method accepts an optional parameter depth, which specifies how deep a nested array structure should be flattened (default to 1).

编辑 #2:根据最新的Array.prototype.flat提案,这非常容易。数组方法接受一个可选参数depth,该参数指定嵌套数组结构应展平的深度(默认为1)。

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

So to flatten an array of arbitrary depth, just call flatmethod with Infinity.

因此,要展平任意深度的数组,只需flat使用Infinity.

[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flat(Infinity) // [0, 1, 2, 3, 4, 5]

回答by Inkling

ES6-style with recursion:

ES6 风格的递归:

function flatten(arr) {
  const flat = [].concat(...arr);
  return flat.some(Array.isArray) ? flatten(flat) : flat;
}

Not that efficient, so don't use for anything more than relatively trivial cases, but it looks kinda nice at least.

效率不高,所以除了相对微不足道的情况外,不要使用任何东西,但至少看起来还不错。



June 2018 Update:

2018 年 6 月更新:

There is now an ES proposal for an Array.prototype.flatmethod. It is currently at stage 3, meaning it's likely to be implemented by browsers soon(ish) and make it into the spec in its current form. There are probably some polyfills floating around.

现在有一个关于Array.prototype.flat方法的 ES 提议。它目前处于第 3 阶段,这意味着它很可能很快就会被浏览器实现(ish)并以当前形式将其纳入规范。可能有一些 polyfill 漂浮在周围。

Example:

例子:

const nested = [[[0], [1]], [[2], [3]], [[4], [5]]];
const flattened = nested.flat(2);  // Need to specify depth if > 1

回答by axelduch

This is an alternative to recursion (see jsfiddle here) and should accept any level of depth which avoids stack overflow.

这是递归的替代方法(请参阅此处的 jsfiddle),并且应该接受避免堆栈溢出的任何深度级别。

var array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]];
console.log(flatten(array), array); // does not mutate array
console.log(flatten(array, true), array); // array is now empty

// This is done in a linear time O(n) without recursion
// memory complexity is O(1) or O(n) if mutable param is set to false
function flatten(array, mutable) {
    var toString = Object.prototype.toString;
    var arrayTypeStr = '[object Array]';
    
    var result = [];
    var nodes = (mutable && array) || array.slice();
    var node;

    if (!array.length) {
        return result;
    }

    node = nodes.pop();
    
    do {
        if (toString.call(node) === arrayTypeStr) {
            nodes.push.apply(nodes, node);
        } else {
            result.push(node);
        }
    } while (nodes.length && (node = nodes.pop()) !== undefined);

    result.reverse(); // we reverse result to restore the original order
    return result;
}

回答by matanso

ES6 one-liner:

ES6 单线:

function flatten(a) {
    return Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
}

Also, non-recursive version for very deep arrays (not very efficient but rather elegant)

此外,非常深的数组的非递归版本(效率不高但相当优雅)

function flatten(a) {
    var queue = a.slice();
    var result = [];
    while(queue.length) {
        let curr = queue.pop();
        if(Array.isArray(curr)) {
            queue.push(...curr);
        }
        else result.push(curr);
    }
    return result;
}

回答by Timothy Gu

ES2019 solution:

ES2019 解决方案

ary.flat(Infinity);

That's it. It's that simple. You can change Infinityto be the appropriate level of flattenedness if you wish.

就是这样。就这么简单。Infinity如果您愿意,您可以更改为适当的扁平度级别。

Example:

例子:

console.log([[[0], [1]], [[2], [3]], [[4], [5]]].flat(Infinity));

Other, longer solutions that work with older browsersbelow.

其他适用于以下旧浏览器的更长的解决方案。



Based on @Leo's solution, but faster by reusing the same array and preventing .concat

基于@Leo 的解决方案,但通过重用相同的数组并防止速度更快 .concat

function flatten(ary, ret = []) {
    for (const entry of ary) {
        if (Array.isArray(entry) {
            flatten(entry, ret);
        } else {
            ret.push(entry);
        }
    }
    return ret;
}
console.log(flatten([[[0], [1]], [[2], [3]], [[4], [5]]]));

Or with Array.prototype.reduce, since you mentioned it:

或者使用Array.prototype.reduce,因为你提到了它:

function flatten(ary, ret = []) {
    return ary.reduce((ret, entry) => {
        if (Array.isArray(entry)) {
            flatten(entry, ret);
        } else {
            ret.push(entry);
        }
        return ret;
    }, ret);
}
console.log(flatten([[[0], [1]], [[2], [3]], [[4], [5]]]));

回答by artnikpro

Flattens 2 levels only:

仅展平 2 个级别:

var arr = [1, [2, 3], [4, 5, 6]];
[].concat.apply([], arr) // -> [1, 2, 3, 4, 5, 6]

回答by Jamie Weber

Inspired by code from Eloquent JavaScriptand the answer provided by @axelduch (A lot more efficient from what I can tell too.)

灵感来自Eloquent JavaScript的代码和@axelduch 提供的答案(据我所知,效率更高。)

function flatten(array, mutable) {
  var nodes = (mutable && array) || array.slice(); // return a new array.
  var flattened = [];

  for (var node = nodes.shift(); node !== undefined; node = nodes.shift()) {
    if (Array.isArray(node)) {
      nodes.unshift.apply(nodes, node);
    } else {
      flattened.push(node);
    }
  }

  return flattened;
}

回答by Adam

Disclaimer: I know that it is an old and already answered question, but @Nick has got me into it as I have commented his answer as one of the most expensive way to flatten an array. I haven't been coding JavaScript for years, but it's like riding a bike - once you learn you'll never forget ;)

免责声明:我知道这是一个古老且已经回答的问题,但@Nick 让我参与其中,因为我将他的答案评论为展平数组的最昂贵的方法之一。我已经多年没有编写 JavaScript 代码了,但这就像骑自行车 - 一旦你学会了,你就永远不会忘记 ;)

Here is my full recursive code (no forloop needed):

这是我的完整递归代码(不需要for循环):

var flattened = [];
function flatten(a, i) {
    if(a.length > i) {
        if(Array.isArray(a[i]))
            flatten(a[i], 0);
        else
            flattened.push(a[i]);
        flatten(a, i + 1);
    }
}

flatten([[0, 1], [2, 3], [4, 5]], 0);
console.log(flattened);

I have tested it against toString().split(',')solution and mine is about 7 times faster. That's what I mean when talking about expensiveness;)

我已经针对toString().split(',')解决方案对其进行了测试,我的解决方案快了大约 7 倍。这就是我在谈论昂贵时的意思;)

回答by dashambles

I like my solution :)

我喜欢我的解决方案:)

var flattenClosure = function(a) {
    var store = [];

    return function() {
        var internMapper = function(b) {
            if (Array.isArray(b)) {
                return b.map(internMapper);
            }
            store.push(b);
            return b;
        }
        a.map(internMapper);
        return store;
    }
};

console.log(flattenClosure([[[[[[[[1]]]], [2], [4], [6, 8, 9], 2]]], 10, 11, [15, 17, 20], [], 33])());

回答by Johan Meester

This is already answered but I am just studying JS and wonder what about:

这已经得到了回答,但我只是在学习 JS 并想知道:

    var array = [[[0], [1]], [[2], [3]], [[4], [5]]];
    var flattend = array.join(",").split(",");
    console.log(flattend);

Only side affect is that the join converts all items to strings, but that can be easilly fixed

唯一的副作用是连接将所有项目转换为字符串,但这可以很容易地修复