ChatGPT解决这个技术问题 Extra ChatGPT

我有一个 JavaScript 数组,例如:

[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]

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

["$6", "$12", "$25", ...]
使用 reduce + concat 的所有解决方案都是 O((N^2)/2),其中作为可接受的答案(只需一次调用 concat)在 a 上最多为 O(N*2)坏的浏览器和好的浏览器 O(N)。 Denys 解决方案还针对实际问题进行了优化,比单个 concat 快 2 倍。对于 reduce 的人来说,编写小代码感觉很酷很有趣,但例如,如果数组有 1000 个单元素子数组,则所有 reduce+concat 解决方案都将执行 500500 operations,而单个 concat 或简单循环将执行 1000 次操作。
使用支持 ES2019 的最新浏览器:array.flat(Infinity) 其中 Infinity 是展平的最大深度。
我很高兴他们把最大的深度.. :D

s
str

您可以使用 concat 来合并数组:

var 数组 = [ ["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"] ]; var 合并 = [].concat.apply([], 数组);控制台.log(合并);

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

var merged2 = [].concat(["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]);

还有 Array.prototype.flat() 方法(在 ES2019 中引入),您可以使用它来展平数组,尽管 it is only available in Node.js starting with version 11, and not at all in Internet Explorer

常量数组 = [ ["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"] ]; const merge3 = arrays.flat(1); //指定嵌套数组结构应该展平的深度级别。默认为 1。 console.log(merge3);


Array.prototype.concat.apply([], arrays)
注意:这个答案只会变平一层。对于递归展平,请参阅@Trindaz 的答案。
除了@Sean 的评论:ES6 语法使这个超级简洁:var merged = [].concat(...arrays)
基于@Sethi 的评论:Array.prototype.concat(...arrays)。此版本适用于 Typescript 的 2.3.0 --strict 模式。不适用于嵌套数组(它不是递归的)。
'apply' 将在某些 vm 的大输入上堆栈溢出,例如 v8。它真的不适合这个用例。
N
Noah Freitas

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

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

用法:

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]

此解决方案的内存使用配置文件是什么?看起来它在尾递归期间创建了很多中间数组......
为什么有一个空数组作为参数传递?没有它,代码就会中断,但它有什么作用?
@ayjay,它是 reduce 函数的起始累加器值,mdn 称为初始值。在这种情况下,它是第一次调用传递给 reduce 的匿名函数时 flat 的值。如果未指定,则第一次调用 reduce 会将数组中的第一个值绑定到 flat,这最终会导致在两个示例中 1 都绑定到 flat1.concat 不是函数。
或者以更短、更性感的形式:const flatten = (arr) => arr.reduce((flat, next) => flat.concat(next), []);
对@TsvetomirTsonev 和 Noah 的任意嵌套解决方案进行反复讨论:const flatten = (arr) => arr.reduce((flat, next) => flat.concat(Array.isArray(next) ? flatten(next) : next), []);
N
Nikita Volkov

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

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


我不太相信这是“高性能的”,因为我似乎在这种调用中遇到了堆栈溢出(在具有 200K 条目的数组上,这些条目是文件中的行)。
如果您可以使用 ES2015,您还可以使用数组扩展来更容易地编写它:[].concat(...[ [1],[2,3],[4] ])
不适用于数组 [2, [3, [4, [5, [6, [7, [8]]]]]]]
很巧妙的方法,我喜欢!如果您必须在数组内部展平数组,则非常有用,因为使用 apply 会假定您正在传递参数数组。唯一令人遗憾的是,如果您的数组中有超过两层深度的数组。
我喜欢这种方法...这里用于 n 维数组:flat = (e) => Array.isArray(e)? [].concat.apply([], e.map(flat)) : e
D
Daniel Wolf

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

var arrays = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]];

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

或者,使用 ES2015:

arrays = arrays.reduce((a, b) => a.concat(b), []);

js-fiddle

Mozilla docs


由于您使用 ES6,因此您也可以使用 the spread-operator as array literalarrays.reduce((flatten, arr) => [...flatten, ...arr])
A
Alister

有一个名为 flat 的新本机方法可以准确地执行此操作。

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

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

很遗憾,这甚至不在答案的第一页上。此功能在 Chrome 69 和 Firefox 62 中可用(以及在后端工作的 Node 11)
-1;不,这不是 ECMAScript 2018 的一部分。它仍然只是一个尚未成为任何 ECMAScript 规范的提案。
我认为现在我们可以考虑这个..因为现在它是标准(2019)的一部分..我们可以重新审视一下这个的性能部分吗?
似乎任何微软浏览器都不支持它(至少在我写这篇评论的时候)
M
Michał Perłakowski

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

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

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

例子

巨大的阵列

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

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

嵌套数组

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

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

具有不同嵌套级别的数组

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

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


除了你的巨大阵列非常平坦。此解决方案不适用于深度嵌套的数组。没有递归解决方案。事实上,除了 Safari 之外,目前没有任何浏览器具有 TCO,因此没有递归算法能够很好地执行。
@nitely 但是在现实世界的情况下,您会有多个嵌套级别的数组吗?
通常,当数组是从用户生成的内容中生成时。
@0xcaff 在 Chrome 中,它根本不适用于 200 000 个元素的数组(您会得到 RangeError: Maximum call stack size exceeded)。对于 20 000 个元素的数组,它需要 2-5 毫秒。
这个的 O 表示法复杂度是多少?
C
Community

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

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

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

你也可以试试这个进行深度展平:

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

请参阅 JSBin 上的演示。

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

扩展运算符

箭头函数

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


因为这里几乎所有的回复都以这种方式滥用 apply,所以我从你的评论中删除了我的评论。我仍然认为以这种方式使用 apply/spread 是不好的建议,但由于没有人关心...
@LUH3417 不是这样的,非常感谢您的评论。事实证明你是对的——这个解决方案确实不适用于大型数组。我发布了 another answer,即使使用 200 000 个元素的数组也能正常工作。
如果您使用的是 ES6,您可以进一步简化为:const flatten = arr => [].concat(...arr)
你是什么意思“不适用于大型阵列”?多大?怎么了?
@GEMI 例如,尝试使用此方法展平 500000 个元素的数组会给出“RangeError:超出最大调用堆栈大小”。
T
Todd Yandell

您可以使用 Underscore

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

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

1+ - 您还可以指定您想要一个浅扁平化数组 by specifying true for the second argument
M
Mulan

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

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

// 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 // 展平:: [[a]] -> [a] const flatten = concatMap (id) // 你的样本数据 const data = [["$6"], ["$12"], ["$25"], ["$25" ], ["$18"], ["$22"], ["$10"]] console.log (flatten (data))

远见

是的,你猜对了,它只变平了一层,这正是它应该如何工作的

想象一些这样的数据集

// Player :: (String, Number) -> Player const Player = (name,number) => [ name, number ] // team :: ( . Player) -> Team const Team = (...players) = > 玩家 // Game :: (Team, Team) -> Game const Game = (teamA, teamB) => [ teamA, teamB ] // 样本数据 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 ] ] ]

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

const gamePlayers = game =>
  flatten (game)

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

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

const gamePlayers = game =>
  badGenericFlatten(game)

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

滚得很深,宝贝

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

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

// 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 // 展平:: [[a]] -> [a] const flatten = concatMap (id) // deepFlatten :: [[a]] -> [a] const deepFlatten = concatMap (x => Array.isArray (x) ? deepFlatten (x) : x) // 你的样本数据 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]

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

如果您不喜欢 deepFlatten 这个名字,也许您可以称它为 obliteratenuke

不要重复两次!

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

使用我称为 mapReduce 的可靠组合器有助于将迭代保持在最小;它接受一个映射函数 m :: a -> b、一个归约函数 r :: (b,a) ->b 并返回一个新的归约函数 - 这个组合器是 transducers 的核心;如果您有兴趣,I've written other answers about them

// 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) // 你的样本数据 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]


通常,当我看到您的回复时,我想撤回我的回复,因为它们已经变得毫无价值。很好的答案! concat 本身不会炸毁堆栈,只有 ...apply 会炸毁(以及非常大的数组)。我没看到。我现在只是觉得很可怕。
请注意,Javascript 中的 concat 与 Haskell 中的含义不同。 Haskell 的 concat ([[a]] -> [a]) 在 Javascript 中将被称为 flatten 并被实现为 foldr (++) [](Javascript:foldr(concat) ([]) 假设为 curried 函数)。 Javascript 的 concat 是一个奇怪的附加(Haskell 中的 (++)),它可以同时处理 [a] -> [a] -> [a]a -> [a] -> [a]
我想一个更好的名字是 flatMap,因为这正是 concatMap 的含义:list monad 的 bind 实例。 concatpMap 实现为 foldr ((++) . f) []。翻译成 Javascript:const flatMap = f => foldr(comp(concat) (f)) ([])。这当然类似于您没有 comp 的实现。
该算法的复杂性是多少?
D
Denys Séguret

要展平单个元素数组的数组,您不需要导入库,一个简单的循环既是最简单的也是 most efficient 解决方案:

for (var i = 0; i < a.length; i++) {
  a[i] = a[i][0];
}

致反对者:请阅读问题,不要反对,因为它不适合您非常不同的问题。对于所提出的问题,此解决方案是最快和最简单的。


它有多神秘并不重要。此代码将此 ['foo', ['bar']] “扁平化”为 ['f', 'bar']
出色地。当然。这是对问题的回答。数组数组中有什么不清楚的地方?这个答案并没有试图回答一个效率较低的更普遍的问题......
确实,你是对的。我过于关注其他例子——没有明确地关注措辞。
R
Ry-

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

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

这种方法在展平您从 JsonPath query 获得的结果集的嵌套数组形式方面非常有效。
如果我们手动传入第二个参数,这将中断。例如,试试这个:flattenArrayOfArrays (arr, 10) 或这个 flattenArrayOfArrays(arr, [1,[3]]); - 这些第二个参数被添加到输出中。
d
diziaq

函数式风格的另一个 ECMAScript 6 解决方案:

声明一个函数:

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

并使用它:

flatten( [1, [2,3], [4,[5,[6]]]] ) // -> [1,2,3,4,5,6]

const flatten = arr => arr.reduce( (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [] ); console.log( 展平([1, [2,3], [4,[5],[6,[7,8,9],10],11],[12],13]) )

还要考虑在现代浏览器的最新版本中提供的本机函数 Array.prototype.flat()(ES6 提案)。 感谢@(Константин Ван) 和@(Mark Amery) 在评论中提到它。

flat 函数有一个参数,指定预期的数组嵌套深度,默认等于 1

[1, 2, [3, 4]].flat();                  // -> [1, 2, 3, 4]

[1, 2, [3, 4, [5, 6]]].flat();          // -> [1, 2, 3, 4, [5, 6]]

[1, 2, [3, 4, [5, 6]]].flat(2);         // -> [1, 2, 3, 4, 5, 6]

[1, 2, [3, 4, [5, 6]]].flat(Infinity);  // -> [1, 2, 3, 4, 5, 6]

让 arr = [1, 2, [3, 4]];控制台.log(arr.flat()); arr = [1, 2, [3, 4, [5, 6]]];控制台.log(arr.flat());控制台.log(arr.flat(1)); console.log(arr.flat(2)); console.log(arr.flat(Infinity));


这很好,很整洁,但我认为你做了 ES6 过量。外部函数不需要是箭头函数。我会坚持使用箭头函数来进行 reduce 回调,但 flatten 本身应该是一个正常的函数。
@StephenSimpson,但外部函数是否需要非箭头函数? “扁平化本身应该是一个正常的功能”——“正常”是指“非箭头”,但为什么呢?为什么在调用中使用箭头函数来减少呢?你能提供你的推理路线吗?
@naomik 我的理由是没有必要。这主要是风格问题。我的评论应该更清楚。使用其中一个或另一个没有主要的编码原因。但是,该功能更易于查看和阅读为非箭头。内部函数可用作箭头函数,因为它更紧凑(当然也没有创建上下文)。箭头函数非常适合创建简洁易读的函数并避免这种混淆。但是,当非箭头就足够时,它们实际上会使阅读变得更加困难。其他人可能不同意!
获得 RangeError: Maximum call stack size exceeded
@Matt,请分享您用来重现错误的环境
k
kabirbaidhya

您也可以尝试新的 Array.flat() 方法。它以下列方式工作:

让 arr = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]].flat()控制台.log(arr);

flat() 方法创建一个新数组,其中所有子数组元素递归连接到其中,直到 1 层深度(即数组内的数组)

如果您还想展平 3 维甚至更高维的数组,您只需多次调用 flat 方法。例如(3 个维度):

让 arr = [1,2,[3,4,[5,6]]].flat().flat().flat();控制台.log(arr);

当心!

Array.flat() 方法相对较新。像 ie 这样的旧浏览器可能没有实现该方法。如果您希望您的代码可以在所有浏览器上工作,您可能必须将您的 JS 转换为旧版本。检查 MDN web docs 以了解当前浏览器的兼容性。


要扁平化高维数组,您只需调用带有 Infinity 参数的扁平化方法。像这样:arr.flat(Infinity)
r
rab

使用 JavaScript 1.8reduce(callback[, initialValue]) 方法怎么样

list.reduce((p,n) => p.concat(n),[]);

会做这项工作。


Y
YairTawil
const common = arr.reduce((a, b) => [...a, ...b], [])

C
Code Maniac

您可以将 Array.flat()Infinity 一起用于任何深度的嵌套数组。

var arr = [ [1,2,3,4], [1,2,[1,2,3]], [1,2,3,4,5,[1,2,3,4,[1 ,2,3,4]]], [[1,2,3,4], [1,2,[1,2,3]], [1,2,3,4,5,[1,2 ,3,4,[1,2,3,4]]]]];让 flatten = arr.flat(Infinity) console.log(flatten)

在此处查看浏览器 compatibility


佚名

请注意:当使用 Function.prototype.apply ([].concat.apply([], arrays)) 或扩展运算符 ([].concat(...arrays)) 来展平数组时,两者都可能导致大型数组的堆栈溢出,因为函数存储在堆栈中。

这是一个功能风格的堆栈安全实现,它权衡了最重要的需求:

可重用性

可读性

简明

表现

// 小的、可重用的辅助函数: const foldl = f => acc => xs => xs.reduce(uncurry(f), acc); // 又名 reduce const uncurry = f => (a, b) => f(a) (b); const concat = xs => y => xs.concat(y); // 展平数组的实际函数 - 不言自明的一行: const flatten = xs => foldl(concat) ([]) (xs); // 任意数组大小(直到堆爆炸 :D) const xs = [[1,2,3],[4,5,6],[7,8,9]];控制台日志(扁平化(xs)); // 为深度嵌套的数组导出递归解决方案现在很简单 // 还有更小的、可重用的辅助函数: const map = f => xs => xs.map(apply(f));常量应用 = f => a => f(a);常量 isArray = Array.isArray; // 派生的递归函数: const flattenr = xs => flatten(map(x => isArray(x) ? flattenr(x) : x) (xs));常量 ys = [1,[2,[3,[4,[5],6,],7],8],9]; console.log(flattenr(ys));

一旦你习惯了柯里化形式的小箭头函数、函数组合和高阶函数,这段代码读起来就像散文一样。然后,编程仅包括将始终按预期工作的小构建块组合在一起,因为它们不包含任何副作用。


哈哈。完全尊重您的回答,尽管阅读这样的函数式编程对我(说英语的人)来说仍然像逐字阅读日语一样。
如果您发现自己在语言 B 中实现语言 A 的功能,而不是作为项目的一部分,其唯一目标就是这样做,那么某个地方的某个人走错了路。会是你吗?仅使用 const flatten = (arr) => arr.reduce((a, b) => a.concat(b), []); 即可为您节省视觉垃圾向您的队友解释为什么您需要 3 个额外函数一些函数调用。
@Daerdemandt 但是如果你把它写成单独的函数,你可能可以在其他代码中重用它们。
@MichałPerłakowski 如果您需要在多个地方使用它们,那么不要重新发明轮子并从 these 中选择一个包 - 由其他人记录和支持。
尴尬而缓慢。
z
zurfyx

ES6 单行展平

请参阅 lodash flattenunderscore flatten (shallow true)

function flatten(arr) {
  return arr.reduce((acc, e) => acc.concat(e), []);
}

or

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

经测试

test('already flatted', () => {
  expect(flatten([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]);
});

test('flats first level', () => {
  expect(flatten([1, [2, [3, [4]], 5]])).toEqual([1, 2, [3, [4]], 5]);
});

ES6 单行深度展平

请参阅 lodash flattenDeepunderscore flatten

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

经测试

test('already flatted', () => {
  expect(flattenDeep([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]);
});

test('flats', () => {
  expect(flattenDeep([1, [2, [3, [4]], 5]])).toEqual([1, 2, 3, 4, 5]);
});

您的第二个示例最好写为 Array.prototype.concat.apply([], arr),因为您创建一个额外的数组只是为了访问 concat 函数。运行时在运行它时可能会或可能不会优化它,但在原型上访问函数看起来并不比现在更难看。
C
Catfish

使用扩展运算符:

常量输入 = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];常量输出 = [].concat(...输入);控制台.log(输出); // --> ["$6", "$12", "$25", "$25", "$18", "$22", "$10"]


R
Ravi Ashara

var 数组 = [["$6"], ["$12","$16"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]] var a = array.flat(Infinity);控制台.log(a);


F
Florian Salihovic

如果您只有包含 1 个字符串元素的数组:

[["$6"], ["$12"], ["$25"], ["$25"]].join(',').split(',');

将完成这项工作。与您的代码示例特别匹配的 Bt。


谁投了反对票,请解释原因。我正在寻找一个体面的解决方案,并且在所有解决方案中我最喜欢这个。
@Anonymous我没有否决它,因为它在技术上符合问题的要求,但这可能是因为这是一个非常糟糕的解决方案,在一般情况下没有用处。考虑到这里有多少更好的解决方案,我永远不会建议有人使用这个,因为它会在您拥有多个元素或它们不是字符串的那一刻中断。
它不仅处理具有 1 个字符串元素的数组,它还处理这个数组 ['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',')
我自己发现了这种方法,但知道它一定已经记录在某个地方,我的搜索到此结束。此解决方案的缺点是,它将数字、布尔值等强制转换为字符串,请尝试 [1,4, [45, 't', ['e3', 6]]].toString().split(',') ---- 或 ----- [1,4, [45, 't', ['e3', 6], false]].toString().split(',')
l
le_m

我推荐一个节省空间的 generator function

函数* flatten(arr) { if (!Array.isArray(arr)) yield arr; else for (let el of arr) yield* flatten(el); } // 示例:console.log(...flatten([1,[2,[3,[4]]]])); // 1 2 3 4

如果需要,创建一个扁平值数组,如下所示:

let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]

我喜欢这种方法。类似于 stackoverflow.com/a/35073573/1175496 ,但使用扩展运算符 ... 来迭代生成器。
R
Redu

Haskellesque 方法

函数 flatArray([x,...xs]){ 返回 x ? [...Array.isArray(x) ? flatArray(x) : [x], ...flatArray(xs)] : []; } var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10]; fa = flatArray(na);控制台.log(fa);


b
balajivijayan

我已经使用递归和闭包完成了它

function flatten(arr) {

  var temp = [];

  function recursiveFlatten(arr) { 
    for(var i = 0; i < arr.length; i++) {
      if(Array.isArray(arr[i])) {
        recursiveFlatten(arr[i]);
      } else {
        temp.push(arr[i]);
      }
    }
  }
  recursiveFlatten(arr);
  return temp;
}

简单而甜蜜,这个答案比公认的答案更好。它将深度嵌套的级别展平到,而不仅仅是第一级
AFAIK 是词法作用域而不是闭包
@dashambles 是正确的 - 不同之处在于,如果它是一个闭包,您会将内部函数返回到外部,并且当外部函数完成后,您仍然可以使用内部函数来访问其范围。这里外部函数的生命周期比内部函数的生命周期长,因此永远不会创建“闭包”。
A
Artem Gavrysh

ES6方式:

const flatten = arr => arr.reduce((acc, next) => acc.concat(Array.isArray(next) ? flatten(next) : next), []) const a = [1, [2, [3 , [4, [5]]]]] console.log(flatten(a))

flatten 函数的 ES5 方式与 N 次嵌套数组的 ES3 后备:

var flatten = (function() { if (!!Array.prototype.reduce && !!Array.isArray) { return function(array) { return array.reduce(function(prev, next) { return prev.concat(Array. isArray(next) ? flatten(next) : next); }, []); }; } else { return function(array) { var arr = []; var i = 0; var len = array.length; var target ; for (; i < len; i++) { target = array[i]; arr = arr.concat( (Object.prototype.toString.call(target) === '[object Array]') ? flatten(target) : 目标); } 返回 arr; }; } }()); var a = [1, [2, [3, [4, [5]]]]];控制台.log(扁平化(a));


H
Holly Cummins

如果你使用 lodash,你可以使用它的 flatten 方法:https://lodash.com/docs/4.17.14#flatten

lodash 的好处是它还有扁平化数组的方法:

i) 递归:https://lodash.com/docs/4.17.14#flattenDeep

ii) 最多 n 层嵌套:https://lodash.com/docs/4.17.14#flattenDepth

例如

const _ = require("lodash");
const pancake =  _.flatten(array)

a
ashwell

前几天我在和 ES6 Generators 开玩笑,写了 this gist。其中包含...

function flatten(arrayOfArrays=[]){
  function* flatgen() {
    for( let item of arrayOfArrays ) {
      if ( Array.isArray( item )) {
        yield* flatten(item)
      } else {
        yield item
      }
    }
  }

  return [...flatgen()];
}

var flatArray = flatten([[1, [4]],[2],[3]]);
console.log(flatArray);

基本上我正在创建一个循环遍历原始输入数组的生成器,如果它找到一个数组,它使用 yield* 运算符结合递归来不断展平内部数组。如果该项目不是一个数组,它只是 yields 单个项目。然后使用 ES6 Spread operator(又名 splat 运算符)我将生成器展平为一个新的数组实例。

我还没有测试过它的性能,但我认为这是使用生成器和 yield* 运算符的一个很好的简单示例。

但同样,我只是在胡说八道,所以我确信有更多高效的方法可以做到这一点。


V
Vlad Ankudinov

只是没有 lodash 的最佳解决方案

let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))

v
vsync

我宁愿将整个数组按原样转换为字符串,但与其他答案不同,我会使用 JSON.stringify 而不是使用 toString() 方法,这会产生不需要的结果。

使用该 JSON.stringify 输出,剩下的就是删除所有括号,用 start & 包装结果再次结束括号,并使用 JSON.parse 提供结果,使字符串恢复“生命”。

可以处理无限的嵌套数组,而无需任何速度成本。

可以正确处理包含逗号的字符串的数组项。

var arr = ["abc",[[[6]]],["3,4"],"2"]; var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]"; var flattened = JSON.parse(s); console.log(扁平化)

仅适用于字符串/数字的多维数组(不是对象)


您的解决方案不正确。展平内部数组 ["345", "2", "3,4", "2"] 时它将包含逗号,而不是将这些值中的每一个分隔为单独的索引
@realseanp - 您误解了该 Array 项中的值。我故意将该逗号作为一个值而不是作为数组分隔符逗号,以强调我的解决方案高于所有其他解决方案的力量,这将输出 "3,4"
我确实误会了
这似乎绝对是我见过的最快的解决方案;您是否知道@vsync 的任何陷阱(当然,除了它看起来有点笨拙的事实 - 将嵌套数组视为字符串:D)
@GeorgeKatsanos - 此方法不适用于不是原始值的数组项(和嵌套项),例如指向 DOM 元素的项
T
TopW3

我认为 array.flat(Infinity) 是一个完美的解决方案。但是平面函数是一个比较新的函数,可能无法在旧版本的浏览器中运行。我们可以使用递归函数来解决这个问题。

const arr = ["A", ["B", [["B11", "B12", ["B131", "B132"]], "B2"]], "C", ["D", " E", "F", ["G", "H", "I"]]] const flatArray = (arr) => { const res = [] for (const item of arr) { if (Array.isArray( item)) { const subRes = flatArray(item) res.push(...subRes) } else { res.push(item) } } return res } console.log(flatArray(arr))


谢谢刚才面试官问了我同样的问题。