Javascript 合并/展平数组数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10865025/
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-24 03:18:59  来源:igfitidea点击:

Merge/flatten an array of arrays

javascriptarraysflatten

提问by Andy

I have a JavaScript array like:

我有一个 JavaScript 数组,如:

[[""], [""], [""], [""], [""], [""], [""]]

How would I go about merging the separate inner arrays into one like:

我将如何将单独的内部数组合并为一个,例如:

["", "", "", ...]

回答by Gumbo

You can use concatto merge arrays:

您可以使用concat合并数组:

var arrays = [
  [""],
  [""],
  [""],
  [""],
  [""],
  [""],
  [""]
];
var merged = [].concat.apply([], arrays);

console.log(merged);

Using the applymethod of concatwill just take the second parameter as an array, so the last line is identical to this:

使用 的apply方法concat只会将第二个参数作为数组,因此最后一行与此相同:

var merged2 = [].concat([""], [""], [""], [""], [""], [""], [""]);


There is also the Array.prototype.flat()method (introduced in ES2019) which you could use to flatten the arrays, although it is only available in Node.js starting with version 11, and not at all in Internet Explorer.

还有Array.prototype.flat()一种方法(在 ES2019 中引入)可用于展平数组,尽管它仅在 Node.js 版本 11 开始可用,而在 Internet Explorer 中根本不可用

const arrays = [
      [""],
      [""],
      [""],
      [""],
      [""],
      [""],
      [""]
    ];
const merge3 = arrays.flat(1); //The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
console.log(merge3);
    

回答by Noah Freitas

Here's a short function that uses some of the newer JavaScript array methods to flatten an n-dimensional array.

这是一个简短的函数,它使用一些较新的 JavaScript 数组方法来展平 n 维数组。

function flatten(arr) {
  return arr.reduce(function (flat, toFlatten) {
    return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
  }, []);
}

Usage:

用法:

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

回答by Nikita Volkov

There is a confusingly hidden method, which constructs a new array without mutating the original one:

有一种令人困惑的隐藏方法,它在不改变原始数组的情况下构造一个新数组:

var oldArray = [[1],[2,3],[4]];
var newArray = Array.prototype.concat.apply([], oldArray);
console.log(newArray); // [ 1, 2, 3, 4 ]

回答by user2668376

It can be best done by javascript reduce function.

最好通过 javascript reduce 函数来完成。

var arrays = [[""], [""], [""], [""], [""], [""], [""], ["
arrays = arrays.reduce((a, b) => a.concat(b), []);
"], [""],[""], [""], [""], ["0"], [""], [""], [""], [""]]; arrays = arrays.reduce(function(a, b){ return a.concat(b); }, []);

Or, with ES2015:

或者,使用 ES2015:

const arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

// Flatten 2 levels deep
const arr3 = [2, 2, 5, [5, [5, [6]], 7]];
arr3.flat(2);
// [2, 2, 5, 5, 5, [6], 7];

// Flatten all levels
const arr4 = [2, 2, 5, [5, [5, [6]], 7]];
arr4.flat(Infinity);
// [2, 2, 5, 5, 5, 6, 7];

js-fiddle

js小提琴

Mozilla docs

Mozilla 文档

回答by Alister

There's a new native method called flatto do this exactly.

有一种称为flat的新本地方法可以准确地做到这一点。

(As of late 2019, flatis now published in the ECMA 2019 standard, and core-js@3(babel's library) includes it in their polyfill library)

(截至 2019 年底,flat现已在 ECMA 2019 标准中发布,并且core-js@3(babel 的库)将其包含在他们的 polyfill库中

const flatten = function(arr, result = []) {
  for (let i = 0, length = arr.length; i < length; i++) {
    const value = arr[i];
    if (Array.isArray(value)) {
      flatten(value, result);
    } else {
      result.push(value);
    }
  }
  return result;
};

回答by Micha? Per?akowski

Most of the answers here don't work on huge (e.g. 200 000 elements) arrays, and even if they do, they're slow. polkovnikov.ph's answerhas the best performance, but it doesn't work for deep flattening.

这里的大多数答案不适用于大型(例如 200 000 个元素)数组,即使它们适用,它们也很慢。polkovnikov.ph 的答案具有最佳性能,但它不适用于深度扁平化。

Here is the fastest solution, which works also on arrays with multiple levels of nesting:

这是最快的解决方案,它也适用于具有多级嵌套的数组

flatten(Array(200000).fill([1]));

Examples

例子

Huge arrays

巨大的数组

flatten(Array(2).fill(Array(2).fill(Array(2).fill([1]))));

It handles huge arrays just fine. On my machine this code takes about 14 ms to execute.

它可以很好地处理巨大的数组。在我的机器上,此代码执行大约需要 14 毫秒。

Nested arrays

嵌套数组

flatten([1, [1], [[1]]]);

It works with nested arrays. This code produces [1, 1, 1, 1, 1, 1, 1, 1].

它适用于嵌套数组。此代码生成[1, 1, 1, 1, 1, 1, 1, 1].

Arrays with different levels of nesting

具有不同嵌套级别的数组

function flatten(arr) {
  return [].concat(...arr)
}

It doesn't have any problems with flattening arrays like this one.

像这样的展平阵列没有任何问题。

回答by Micha? Per?akowski

Update: it turned out that this solution doesn't work with large arrays. It you're looking for a better, faster solution, check out this answer.

更新:事实证明,此解决方案不适用于大型阵列。如果您正在寻找更好、更快的解决方案,请查看此答案



function deepFlatten(arr) {
  return flatten(           // return shalowly flattened array
    arr.map(x=>             // with each x in array
      Array.isArray(x)      // is x an array?
        ? deepFlatten(x)    // if yes, return deeply flattened x
        : x                 // if no, return just x
    )
  )
}

Is simply expands arrand passes it as arguments to concat(), which merges all the arrays into one. It's equivalent to [].concat.apply([], arr).

Is 只是扩展arr并将其作为参数传递给concat(),这将所有数组合并为一个。它相当于[].concat.apply([], arr).

You can also try this for deep flattening:

您也可以尝试使用此方法进行深度展平:

var x = [[1], [2], [3, 4]];

_.flatten(x); // => [1, 2, 3, 4]

See demo on JSBin.

请参阅JSBin 上的演示。

References for ECMAScript 6 elements used in this answer:

此答案中使用的 ECMAScript 6 元素的参考资料:



Side note: methods like find()and arrow functions are not supported by all browsers, but it doesn't mean that you can't use these features right now. Just use Babel— it transforms ES6 code into ES5.

旁注:find()并非所有浏览器都支持像箭头函数这样的方法,但这并不意味着您现在不能使用这些功能。只需使用Babel——它将 ES6 代码转换为 ES5。

回答by Todd Yandell

You can use Underscore:

您可以使用下划线

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.map(f).reduce(concat, [])

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)

// your sample data
const data =
  [[""], [""], [""], [""], [""], [""], [""]]

console.log (flatten (data))

回答by Thank you

Generic procedures mean we don't have to rewrite complexity each time we need to utilize a specific behaviour.

通用过程意味着我们不必在每次需要利用特定行为时重写复杂性。

concatMap(or flatMap) is exactly what we need in this situation.

concatMap(或flatMap) 正是我们在这种情况下所需要的。

// Player :: (String, Number) -> Player
const Player = (name,number) =>
  [ name, number ]

// team :: ( . Player) -> Team
const Team = (...players) =>
  players

// Game :: (Team, Team) -> Game
const Game = (teamA, teamB) =>
  [ teamA, teamB ]

// sample data
const teamA =
  Team (Player ('bob', 5), Player ('alice', 6))

const teamB =
  Team (Player ('ricky', 4), Player ('julian', 2))

const game =
  Game (teamA, teamB)

console.log (game)
// [ [ [ 'bob', 5 ], [ 'alice', 6 ] ],
//   [ [ 'ricky', 4 ], [ 'julian', 2 ] ] ]

foresight

远见

And yes, you guessed it correctly, it only flattens onelevel, which is exactly how it shouldwork

是的,你猜对了,它只是变得平坦一个水平,而这正是它是如何工作

Imagine some data set like this

想象一些这样的数据集

const gamePlayers = game =>
  flatten (game)

gamePlayers (game)
// => [ [ 'bob', 5 ], [ 'alice', 6 ], [ 'ricky', 4 ], [ 'julian', 2 ] ]

Ok, now say we want to print a roster that shows all the players that will be participating in game

好的,现在假设我们要打印一个名单,显示所有将参加的球员game......

const gamePlayers = game =>
  badGenericFlatten(game)

gamePlayers (game)
// => [ 'bob', 5, 'alice', 6, 'ricky', 4, 'julian', 2 ]

If our flattenprocedure flattened nested arrays too, we'd end up with this garbage result …

如果我们的flatten过程也扁平化嵌套数组,我们最终会得到这个垃圾结果......

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.map(f).reduce(concat, [])

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)

// deepFlatten :: [[a]] -> [a]
const deepFlatten =
  concatMap (x =>
    Array.isArray (x) ? deepFlatten (x) : x)

// your sample data
const data =
  [0, [1, [2, [3, [4, 5], 6]]], [7, [8]], 9]

console.log (flatten (data))
// [ 0, 1, [ 2, [ 3, [ 4, 5 ], 6 ] ], 7, [ 8 ], 9 ]

console.log (deepFlatten (data))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]


rollin' deep, baby

滚得很深,宝贝

That's not to say sometimes you don't want to flatten nested arrays, too – only that shouldn't be the default behaviour.

这并不是说有时您也不想展平嵌套数组——只是这不应该是默认行为。

We can make a deepFlattenprocedure with ease …

我们可以轻松制作一个deepFlatten程序……

// mapReduce = (a -> b, (b,a) -> b, (b,a) -> b)
const mapReduce = (m,r) =>
  (acc,x) => r (acc, m (x))

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.reduce (mapReduce (f, concat), [])

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)
  
// deepFlatten :: [[a]] -> [a]
const deepFlatten =
  concatMap (x =>
    Array.isArray (x) ? deepFlatten (x) : x)

// your sample data
const data =
  [ [ [ 1, 2 ],
      [ 3, 4 ] ],
    [ [ 5, 6 ],
      [ 7, 8 ] ] ]

console.log (flatten (data))
// [ [ 1. 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]

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

There. Now you have a tool for each job – one for squashing one level of nesting, flatten, and one for obliterating all nesting deepFlatten.

那里。现在您为每项工作提供了一个工具——一个用于压缩一层嵌套,flatten另一个用于消除所有嵌套deepFlatten

Maybe you can call it obliterateor nukeif you don't like the name deepFlatten.

也许你可以叫它,obliterate或者nuke如果你不喜欢这个名字deepFlatten



Don't iterate twice !

不要重复两次!

Of course the above implementations are clever and concise, but using a .mapfollowed by a call to .reducemeans we're actually doing more iterations than necessary

当然,上面的实现既聪明又简洁,但是使用.map后跟调用.reduce意味着我们实际上进行了比必要更多的迭代

Using a trusty combinator I'm calling mapReducehelps keep the iterations to a minium; it takes a mapping function m :: a -> b, a reducing function r :: (b,a) ->band returns a new reducing function - this combinator is at the heart of transducers; if you're interested, I've written other answers about them

使用我调用的可靠组合器mapReduce有助于将迭代保持在最小值;它需要一个映射函数m :: a -> b、一个归约函数r :: (b,a) ->b并返回一个新的归约函数——这个组合子是转换器的核心;如果你有兴趣,我已经写了关于它们的其他答案

function flattenArrayOfArrays(a, r){
    if(!r){ r = []}
    for(var i=0; i<a.length; i++){
        if(a[i].constructor == Array){
            r.concat(flattenArrayOfArrays(a[i], r));
        }else{
            r.push(a[i]);
        }
    }
    return r;
}

回答by Trindaz

A solution for the more general case, when you may have some non-array elements in your array.

更一般情况的解决方案,当您的数组中可能有一些非数组元素时。

##代码##