如何在javascript中展平嵌套数组
How to flatten nested array in javascript?
众所周知,使用方法平展数组[[0, 1], [2, 3], [4, 5]]
reduce()
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
return a.concat(b);
});
那么如何将这个数组[[[0], [1]], [[2], [3]], [[4], [5]]]
展平到[0, 1, 2, 3, 4, 5]
呢?
递归的完美用例,可以处理更深层次的结构:
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]
或者,作为数组方法:
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]
编辑#1:好吧,考虑一下功能化的方式(除了命名递归,它应该使用Y组合器进行纯函数:D(。
function flatten(ary) {
return ary.reduce(function(a, b) {
if (Array.isArray(b)) {
return a.concat(flatten(b))
}
return a.concat(b)
}, [])
}
让我们采用一些 ES6 语法,使其在一行中更短。
const flatten = (ary) => ary.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [])
但请记住,这个不能作为数组方法应用,因为箭头函数没有自己的this
。
编辑#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]
因此,要展平任意深度的数组,只需使用 Infinity
调用flat
方法。
[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flat(Infinity) // [0, 1, 2, 3, 4, 5]
ES6 样式,带递归:
Redacted
<小时 />2018 年 6 月更新:
现在有一个关于Array.prototype.flat
方法的 ES 提案。它目前处于第 3 阶段,这意味着它很可能很快就会被浏览器实现,并以当前的形式进入规范。可能有一些填充物漂浮在周围。
例:
const nested = [[[0], [1]], [[2], [3]], [[4], [5]]];
const flattened = nested.flat(2); // Need to specify depth if > 1
2019年6月更新:
Array.prototype.flat
被正式添加到ES2019规范中的语言中。
这是递归的替代方法(参见此处的 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;
}
ES2019 解决方案:
ary.flat(Infinity);
就是这样。就是这么简单。如果需要,可以将Infinity
更改为适当的平展级别。
例:
console.log([[[0], [1]], [[2], [3]], [[4], [5]]].flat(Infinity));
适用于以下旧版浏览器的其他较长解决方案。
基于 @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]]]));
或者用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]]]));
ES6 单行:
function flatten(a) {
return Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
}
此外,用于非常深的数组的非递归版本(不是很有效,但相当优雅(
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;
}
仅展平 2 个级别:
var arr = [1, [2, 3], [4, 5, 6]];
[].concat.apply([], arr) // -> [1, 2, 3, 4, 5, 6]
这已经回答了,但我只是在学习JS,想知道怎么样:
var array = [[[0], [1]], [[2], [3]], [[4], [5]]];
var flattend = array.join(",").split(",");
console.log(flattend);
唯一的副作用是联接将所有项目转换为字符串,但这可以很容易地修复
我喜欢我的解决方案:)
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])());
如果您知道数组仅由数字组成,则可以执行以下操作:
array.join().split(',').map(Number);
灵感来自 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;
}
免责声明:我知道这是一个古老且已经回答的问题,但@Nick让我陷入困境,因为我已经评论了他的答案是扁平化数组的最昂贵的方法之一。我已经很多年没有编写 JavaScript 代码了,但这就像骑自行车一样 - 一旦你学会了,你永远不会忘记;)
这是我的完整递归代码(不需要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);
我已经针对toString().split(',')
解决方案对其进行了测试,我的速度提高了大约 7 倍。这就是我在谈论昂贵性时的意思;)
function flatten(array) {
return array.reduce(
(previous, current) =>
Array.isArray(current)
? [...previous, ...flatten(current)]
: [...previous, current]
, []
);
}
var nested = [[[0], [1]], [[2], [3]], [[4], [5]]];
var flattened = [].concat.apply([],[].concat.apply([],nested));
console.log('-> flattened now: ' + flattened);
基于dashamble的回答,但我相信这更容易理解:
var steamroller = function(arr) {
var result = [];
var dropHeavyObject = function(auxArr) {
var flatnd = [];
flatnd = auxArr.map(function(x) {
if(Array.isArray(x)) {
return dropHeavyObject(x);
} else {
result.push(x);
return x;
}
});
return flatnd;
};
dropHeavyObject(arr);
return result;
}
lodash 中有三个实用函数与您的问题flatten
、flattenDeep
、flattenDepth in lodash
有关。 flatten
进入一个深度,flattenDeep
一直进入最深的层次,flattenDepth让你选择"多深"来展平。
例:
> var arr = [[[0], [1]], [[2], [3]], [[4], [5]]];
> _.flattenDeep(arr)
[0, 1, 2, 3, 4, 5]
var flattenWithStack = function(arr) {
var stack = [];
var flat = [];
stack.push(arr);
while(stack.length > 0) {
var curr = stack.pop();
if(curr instanceof Array) {
stack = stack.concat(curr);
} else {
flat.push(curr);
}
}
return flat.reverse();
}
非递归。基本上是 dfs。
function flatten(x) {
if (x.length == 0) {return []};
if (Array.isArray(x[0])) {
return flatten(x[0].concat(flatten(x.slice(1,x.length))));
}
return [].concat([x[0]], flatten(x.slice(1,x.length)));
}
递归平展数组。
前几天我遇到了这个问题,发现了一个小技巧......实际上我刚刚意识到它与最后一个答案非常相似,并且像上述答案的评论一样,它不适用于对象,但对于简单的数字等,它工作得很好。
如果将嵌套数组转换为字符串,则会用逗号分隔值。然后,您可以用逗号将其拆分以形成字符串。如果需要将字符串转换为 int 或 float,则可以运行转换每个值的新数组。
stringArray = [[0, 1], [2, 3], [4, 5]].toString().split(',');
stringArray.forEach((v,i,a) => a[i] = parseFloat(a[i]));
式编程的实现
通过函数式编程,我们可以简单地从另一个更通用的函数中导出flatten
:traverse
。
后者是一个遍历和减少任意嵌套数组的函数,就像平面数组一样。这是可能的,因为具有未知深度的嵌套数组只不过是树数据结构的特定版本:
const traverse = f => g => acc => xs => {
let [leaf, stack] = xs[0][0] === undefined
? [xs[0], xs.slice(1)]
: f([]) (xs);
return stack.length
? traverse(f) (g) (g(leaf) (acc)) (stack)
: g(leaf) (acc);
};
const dfs = stack => tree => tree[0] === undefined
? [tree, stack]
: dfs(tree.length > 1 ? concat(stack) (tree.slice(1)) : stack) (tree[0]);
const concat = ys => xs => xs.concat(ys);
const flatten = f => traverse(f) (concat) ([]);
const xs = [[[1,2,3],4,5,6],7,8,[9,10,[11,12],[[13]],14],15];
console.log(flatten(dfs) (xs));
请注意,树数据结构可以按深度优先 (DFS( 或广度优先 (BFS( 遍历。数组通常按深度一阶遍历。
正如我所说traverse
是一个通用的归约函数,就像平面数组的reduce一样。因此,我们也可以轻松计算所有元素的总和:
const add = y => x => x + y;
traverse(dfs) (add) (0) (xs); // 120
结论:要以功能方式扁平化任意嵌套的数组,我们只需要一行:const flatten = f => traverse(f) (concat) ([]);
。所有其他涉及的功能都是通用的,并具有一系列其他潜在应用。这是100%的可重用性!
使用 JSON.stringify
和 JSON.parse
arr = JSON.parse("[" +
JSON.stringify(arr)
.replace(/['[']]+/g,"")
.replace(/,,/g,",") +
"]");
// implementation
function flattenArray(){
const input = arguments[0]
, output = flatten(input)
function flatten(){
return [].concat.apply([], arguments[0])
}
return input.length === output.length
? output
: flattenArray(output)
}
// how to use?
flattenArray([1,2,[3]) // return [1,2,3]
测试用例 ->https://github.com/CHAOWEICHIU/ccw-custom-functions/blob/master/test/units/flattenArray.js
function flatten(arrayOfArrays) {
return arrayOfArrays.reduce(function(flat, subElem) {
return flat.concat(Array.isArray(subElem) ? flatten(subElem) : subElem);
}, []);
}
var arr0 = [0, 1, 2, 3, 4];
var arr1 = [[0,1], 2, [3, 4]];
var arr2 = [[[0, 1], 2], 3, 4];
console.log(flatten(arr0)); // [0, 1, 2, 3, 4]
console.log(flatten(arr1)); // [0, 1, 2, 3, 4]
console.log(flatten(arr2)); // [0, 1, 2, 3, 4]
console.log(flatten([])); // []
使用 Lisp 约定。
但是,使用 .shift(( 和 .concat(( 效率低下。
flatten (array) {
// get first element (car) and shift array (cdr)
var car = array.shift();
// check to see if array was empty
if (car === undefined) {
return [];
// if the first element (car) was an array, recurse on it
} else if (_.isArray(car)) {
return flatten(car).concat(flatten(array));
// otherwise, cons (concatenate) the car to the flattened version of cdr (rest of array)
} else {
return [car].concat(flatten(array))
}
}
如果你有一个无限嵌套的数组,如下所示a
,这就是我会做的。
const a = [[1,2,[3]],4]
Array.prototype.flatten = (array) => {
const newArray = []
const flattenHelper = (array) => {
array.map(i => {
Array.isArray(i) ? flattenHelper(i) : newArray.push(i)
})
}
flattenHelper(a)
return newArray
}
const newArray = a.flatten()
console.log(newArray);
这是我得到的:
function steamrollArray(arr) {
// the flattened array
var newArr = [];
// recursive function
function flatten(arr, newArr) {
// go through array
for (var i = 0; i < arr.length; i++) {
// if element i of the current array is a non-array value push it
if (Array.isArray(arr[i]) === false) {
newArr.push(arr[i]);
}
// else the element is an array, so unwrap it
else {
flatten(arr[i], newArr);
}
}
}
flatten(arr, newArr);
return newArr;
}
这样解决
const array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]];
const flatten(arr) => arr.reduce((acc, item) =>
acc.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
console.log(flatten(array));
请注意,对于深度阵列,应应用 TCO。
具有递归解决方案的 TCO 的版本
const array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]];
const flatten = (() => {
const _flatten = (acc, arr) => arr.reduce((acc, item) => acc.concat(Array.isArray(item) ? _flatten([], item) : item), acc);
return arr => _flatten([], arr);
})();
console.log(flatten(array))
认为这个函数有效。
function flatten(arr){
return arr.reduce(function(a,b){
return [].concat(Array.isArray(a)? flatten(a) :a,Array.isArray(b)? flatten(b):b);
});
}
- 从多维嵌套json数组创建下拉列表
- 如何通过json对象数组为嵌套对象赋值
- 为循环嵌套的Javascript未按预期返回数组
- 具有嵌套对象数组的 Javascript 对象的递归搜索函数
- 将js对象更改为使用嵌套的可观察数组敲除js视图模型
- MongoDB嵌套对象数组后查询
- 从对象数组中动态创建嵌套json
- 使用Angular.js解析JSON中的嵌套对象数组
- 如何将id数组与带下划线的对象数组嵌套属性进行比较
- 将JS对象数组转换为嵌套形式的最有效方法
- 在javascript/angular中创建播放列表(按值复制数组,但按引用设置嵌套对象)
- 嵌套在 ng 重复中的拼接数组
- 嵌套/同心组和mouseenter/mouseleve
- javascript获取嵌套子数组中的对象
- 嵌套捕获组结果
- 如何在嵌套级别不受限制的情况下显示对象的动态嵌套子数组对象
- 按嵌套子数组对 arr 进行排序
- 播放框架 - 表单不绑定到嵌套元组
- 遍历嵌套到组和节中的一组输入
- 在数组和嵌套子数组中搜索Lodash属性