我有一个像这样的JavaScript数组:
[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]
我将如何将单独的内部数组合并为一个类似的数组:
["$6", "$12", "$25", ...]
[].concat(...array)
我有一个像这样的JavaScript数组:
[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]
我将如何将单独的内部数组合并为一个类似的数组:
["$6", "$12", "$25", ...]
[].concat(...array)
Answers:
您可以使用concat
合并数组:
var arrays = [
["$6"],
["$12"],
["$25"],
["$25"],
["$18"],
["$22"],
["$10"]
];
var merged = [].concat.apply([], arrays);
console.log(merged);
使用apply
of方法concat
将仅将第二个参数作为数组,因此最后一行与此相同:
var merged2 = [].concat(["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]);
还有Array.prototype.flat()
一种方法(在ES2019中引入)可用于展平阵列,尽管该方法仅在从版本11开始的Node.js中可用,而在Internet Explorer中完全不可用。
const arrays = [
["$6"],
["$12"],
["$25"],
["$25"],
["$18"],
["$22"],
["$10"]
];
const merge3 = arrays.flat(1); //The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
console.log(merge3);
concat
它不会修改源数组,因此merged
在调用之后,该数组将保持为空concat
。最好这样说:merged = merged.concat.apply(merged, arrays);
var merged = [].concat.apply([], arrays);
似乎可以很好地将它放在一行上。编辑:正如尼基塔的答案已经显示。
Array.prototype.concat.apply([], arrays)
。
var merged = [].concat(...arrays)
这是一个简短的函数,它使用一些较新的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]
flat
传递给的匿名函数的第一次调用中的值reduce
。如果未指定,则第一次调用reduce
将数组中的第一个值绑定到flat
,这最终将导致在两个示例中都将1
其绑定flat
。 1.concat
不是功能。
const flatten = (arr) => arr.reduce((flat, next) => flat.concat(next), []);
const flatten = (arr) => arr.reduce((flat, next) => flat.concat(Array.isArray(next) ? flatten(next) : next), []);
有一个令人迷惑的隐藏方法,该方法构造了一个新数组,而不改变原始数组:
var oldArray = [[1],[2,3],[4]];
var newArray = Array.prototype.concat.apply([], oldArray);
console.log(newArray); // [ 1, 2, 3, 4 ]
[].concat([[1],[2,3],[4]]...)
[[1],[2,3],[4]]
。@Nikita提供的解决方案对于CoffeeScript和JS都是正确的。
[].concat([1],[2,3],[4],...)
。
...
是实际的代码,而不是一些省略号点。
最好通过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), []);
arrays.reduce((flatten, arr) => [...flatten, ...arr])
有一个称为flat的新本机方法可以准确地做到这一点。
(截至2019年末,flat
现已在ECMA 2019标准中发布,并且core-js@3
(babel的库)将其包含在其polyfill 库中)
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];
这里的大多数答案不适用于巨大的数组(例如20万个元素),即使这样做,也很慢。polkovnikov.ph的答案具有最佳性能,但不适用于深度展平。
这是最快的解决方案,它也适用于具有多层嵌套的数组:
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]]]);
展平像这样的数组没有任何问题。
RangeError: Maximum call stack size exceeded
)。对于2万个元素的数组,需要2到5毫秒的时间。
更新:事实证明,该解决方案不适用于大型阵列。如果您正在寻找更好,更快的解决方案,请查看此答案。
function flatten(arr) {
return [].concat(...arr)
}
只是简单地扩展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。
const flatten = arr => [].concat(...arr)
您可以使用Underscore:
var x = [[1], [2], [3, 4]];
_.flatten(x); // => [1, 2, 3, 4]
true
第二个参数来指定要浅化的数组。
通用过程意味着我们不必在每次需要利用特定行为时都重写复杂性。
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
// flatten :: [[a]] -> [a]
const flatten =
concatMap (id)
// your sample data
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) =>
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 ] ] ]
好的,现在说我们要打印一个名单,显示所有将要参加的球员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
// 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 ]
那里。现在,您有一个用于每项工作的工具–一个用于挤压一层嵌套的工具,flatten
另一个用于消除所有嵌套的工具。deepFlatten
。
也许您可以称呼它,obliterate
或者nuke
您不喜欢这个名字deepFlatten
。
不要重复两次!
当然,以上实现都是聪明而简洁的,但是使用.map
后面的调用.reduce
意味着我们实际上进行了不必要的迭代
使用我要调用的可信赖组合器mapReduce
有助于将迭代次数保持在最低限度;它具有映射函数m :: a -> b
,归约函数r :: (b,a) ->b
并返回新的归约函数-该组合器是换能器的核心; 如果您有兴趣,我已经写了其他有关他们的答案
// 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 ]
concat
Javascript与Haskell具有不同的含义。Haskell的concat
([[a]] -> [a]
)将用flatten
Javascript 调用,并实现为foldr (++) []
(Javascript:foldr(concat) ([])
假定为函数)。Javascript concat
是一个怪异的追加((++)
在Haskell中),可以同时处理[a] -> [a] -> [a]
和a -> [a] -> [a]
。
flatMap
,因为这正是concatMap
:Monad 的bind
实例list
。concatpMap
被实现为foldr ((++) . f) []
。翻译成Javascript :const flatMap = f => foldr(comp(concat) (f)) ([])
。当然,这与不带的实现类似comp
。
一种更一般情况的解决方案,当您的数组中可能包含一些非数组元素时。
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;
}
Object.defineProperty(Array.prototype,'flatten',{value:function(r){for(var a=this,i=0,r=r||[];i<a.length;++i)if(a[i]!=null)a[i] instanceof Array?a[i].flatten(r):r.push(a[i]);return r}});
flattenArrayOfArrays (arr, 10)
或执行以下操作flattenArrayOfArrays(arr, [1,[3]]);
-将第二个参数添加到输出中。
r
将实际连接递归的结果。
那使用reduce(callback[, initialValue])
方法呢JavaScript 1.8
list.reduce((p,n) => p.concat(n),[]);
会做的工作。
[[1], [2,3]].reduce( (a,b) => a.concat(b), [] )
更性感。
[[1], [2,3]].reduce( (a,b) => a.concat(b))
功能样式的另一个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( flatten([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]
let arr = [1, 2, [3, 4]];
console.log( arr.flat() );
arr = [1, 2, [3, 4, [5, 6]]];
console.log( arr.flat() );
console.log( arr.flat(1) );
console.log( arr.flat(2) );
console.log( arr.flat(Infinity) );
RangeError: Maximum call stack size exceeded
要展平单元素数组的数组,无需导入库,简单的循环既是最简单的也是 最有效的解决方案:
for (var i = 0; i < a.length; i++) {
a[i] = a[i][0];
}
致拒绝投票的人:请阅读问题,不要拒绝投票,因为它不适合您的非常不同的问题。对于提出的问题,此解决方案既最快又最简单。
['foo', ['bar']]
到['f', 'bar']
。
const common = arr.reduce((a, b) => [...a, ...b], [])
请注意:当Function.prototype.apply
([].concat.apply([], arrays)
)或点差运算符([].concat(...arrays)
)来使数组变平时,由于函数的每个参数都存储在堆栈中,因此两者都可能导致大型数组的堆栈溢出。
这是一种函数式的堆栈安全实现,权衡了最重要的要求:
// small, reusable auxiliary functions:
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc); // aka reduce
const uncurry = f => (a, b) => f(a) (b);
const concat = xs => y => xs.concat(y);
// the actual function to flatten an array - a self-explanatory one-line:
const flatten = xs => foldl(concat) ([]) (xs);
// arbitrary array sizes (until the heap blows up :D)
const xs = [[1,2,3],[4,5,6],[7,8,9]];
console.log(flatten(xs));
// Deriving a recursive solution for deeply nested arrays is trivially now
// yet more small, reusable auxiliary functions:
const map = f => xs => xs.map(apply(f));
const apply = f => a => f(a);
const isArray = Array.isArray;
// the derived recursive function:
const flattenr = xs => flatten(map(x => isArray(x) ? flattenr(x) : x) (xs));
const ys = [1,[2,[3,[4,[5],6,],7],8],9];
console.log(flattenr(ys));
一旦您习惯了咖喱形式的小箭头功能,功能组成和高阶功能,此代码就像散文一样读起来。然后,编程仅由放在一起始终可以按预期运行的小构建块组成,因为它们不包含任何副作用。
const flatten = (arr) => arr.reduce((a, b) => a.concat(b), []);
节省您的视觉垃圾,并向队友解释为什么您需要3个额外的功能以及一些函数调用。
function flatten(arr) {
return arr.reduce((acc, e) => acc.concat(e), []);
}
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]);
});
见lodash flattenDeep,下划线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
函数。运行时在运行时可能会优化它,也可能不会优化它,但是在任何情况下访问原型上的函数看起来都不比现在更难看。
Haskellesque方法
function flatArray([x,...xs]){
return x ? [...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);
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))
ES5 flatten
函数的功能,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) : target
);
}
return arr;
};
}
}());
var a = [1, [2, [3, [4, [5]]]]];
console.log(flatten(a));
如果只有带有1个字符串元素的数组:
[["$6"], ["$12"], ["$25"], ["$25"]].join(',').split(',');
会做的工作。Bt与您的代码示例特别匹配。
['$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(',')
我建议一个节省空间的生成器函数:
function* flatten(arr) {
if (!Array.isArray(arr)) yield arr;
else for (let el of arr) yield* flatten(el);
}
// Example:
console.log(...flatten([1,[2,[3,[4]]]])); // 1 2 3 4
如果需要,创建一个展平值数组,如下所示:
let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]
...
迭代生成器。
我宁愿将整个数组按原样转换为字符串,但与其他答案不同,我会使用JSON.stringify
而不是使用toString()
会产生不良结果的方法。
使用该JSON.stringify
输出,剩下的就是删除所有括号,再次将结果括在起始和结束括号中,然后提供JSON.parse
将字符串带回“ life”的结果。
var arr = ["abc",[[[6]]],["3,4"],"2"];
var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]";
var flattened = JSON.parse(s);
console.log(flattened)
["345", "2", "3,4", "2"]
而不是将这些值中的每一个分隔为单独的索引
"3,4"
。
您也可以尝试新Array.Flat()
方法。它以以下方式工作:
let arr = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]].flat()
console.log(arr);
该flat()
方法创建一个新数组,将所有子数组元素递归连接到该数组中,直至达到1层深度(即数组内部的数组)
如果您还希望展平3维或什至更高维的数组,则只需多次调用flat方法。例如(3个维度):
let arr = [1,2,[3,4,[5,6]]].flat().flat().flat();
console.log(arr);
Array.Flat()
方法相对较新。较旧的浏览器(例如ie)可能尚未实现该方法。如果您希望代码在所有浏览器上都能工作,则可能必须将JS转换为旧版本。检查MD Web文档是否具有当前浏览器兼容性。
Infinity
参数调用flat方法即可。像这样:arr.flat(Infinity)
看起来这似乎是RECURSION的工作!
码:
var flatten = function(toFlatten) {
var isArray = Object.prototype.toString.call(toFlatten) === '[object Array]';
if (isArray && toFlatten.length > 0) {
var head = toFlatten[0];
var tail = toFlatten.slice(1);
return flatten(head).concat(flatten(tail));
} else {
return [].concat(toFlatten);
}
};
用法:
flatten([1,[2,3],4,[[5,6],7]]);
// Result: [1, 2, 3, 4, 5, 6, 7]
flatten(new Array(15000).fill([1]))
抛出Uncaught RangeError: Maximum call stack size exceeded
并冻结了我的devTools 10秒钟
我已经使用递归和闭包完成了
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;
}
前几天,我正和ES6 Generators混在一起,写了这个要点。其中包含...
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 *运算符与递归结合以连续地对内部数组进行展平。如果该项不是数组,则仅产生单个项。然后使用ES6 Spread运算符(又名splat运算符)将生成器展平到一个新的数组实例中。
我还没有测试它的性能,但是我认为这是一个使用生成器和yield *运算符的简单示例。
但是再次,我只是在无聊中,所以我确定还有更多高效的方法可以做到这一点。
没有lodash的最佳解决方案
let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))