[英]Javascript recursive array flattening
我正在练习并尝试编写一个递归数组展平函数。 代码在这里:
function flatten() {
var flat = [];
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] instanceof Array) {
flat.push(flatten(arguments[i]));
}
flat.push(arguments[i]);
}
return flat;
}
问题是,如果我向那里传递一个数组或嵌套数组,我会收到“超出最大调用堆栈大小”错误。 我究竟做错了什么?
问题是你如何传递数组的处理,如果值是一个数组,那么你一直在调用它导致一个无限循环
function flatten() {
var flat = [];
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] instanceof Array) {
flat.push.apply(flat, flatten.apply(this, arguments[i]));
} else {
flat.push(arguments[i]);
}
}
return flat;
}
演示:小提琴
这是一个更现代的版本:
function flatten(items) {
const flat = [];
items.forEach(item => {
if (Array.isArray(item)) {
flat.push(...flatten(item));
} else {
flat.push(item);
}
});
return flat;
}
flat()
array.flat(Infinity)
const array = [1, 1, [2, 2], [[3, [4], 3], 2]]
// All layers
array.flat(Infinity) // [1, 1, 2, 2, 3, 4, 3, 2]
// Varying depths
array.flat() // [1, 1, 2, 2, Array(3), 2]
array.flat(2) // [1, 1, 2, 2, 3, Array(1), 3, 2]
array.flat().flat() // [1, 1, 2, 2, 3, Array(1), 3, 2]
array.flat(3) // [1, 1, 2, 2, 3, 4, 3, 2]
array.flat().flat().flat() // [1, 1, 2, 2, 3, 4, 3, 2]
我可以使用- 95% 22 年 7 月
如果项目是数组,我们只需将所有剩余的项目添加到这个数组中
function flatten(array, result) { if (array.length === 0) { return result } var head = array[0] var rest = array.slice(1) if (Array.isArray(head)) { return flatten(head.concat(rest), result) } result.push(head) return flatten(rest, result) } console.log(flatten([], [])) console.log(flatten([1], [])) console.log(flatten([1,2,3], [])) console.log(flatten([1,2,[3,4]], [])) console.log(flatten([1,2,[3,[4,5,6]]], [])) console.log(flatten([[1,2,3],[4,5,6]], [])) console.log(flatten([[1,2,3],[[4,5],6,7]], [])) console.log(flatten([[1,2,3],[[4,5],6,[7,8,9]]], []))
[...arr.toString().split(",")]
使用Object
的toString()
方法。 使用扩展运算符(...)
创建一个字符串数组并用","
分割它。
例子:
let arr =[["1","2"],[[[3]]]]; // output : ["1", "2", "3"]
哈斯克尔式的方法...
function flatArray([x,...xs]){ return x !== undefined ? [...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);
所以我认为上面的代码片段可以通过适当的缩进更容易理解;
function flatArray([x,...xs]){
return x !== undefined ? [ ...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);
如果你假设你的第一个参数是一个数组,你可以让它变得非常简单。
function flatten(a) {
return a.reduce((flat, i) => {
if (Array.isArray(i)) {
return flat.concat(flatten(i));
}
return flat.concat(i);
}, []);
}
如果您确实想展平多个数组,只需在通过之前将它们连接起来。
如果有人正在寻找扁平化的对象数组(例如tree
),那么这里是一个代码:
function flatten(items) { const flat = []; items.forEach(item => { flat.push(item) if (Array.isArray(item.children) && item.children.length > 0) { flat.push(...flatten(item.children)); delete item.children } delete item.children }); return flat; } var test = [ {children: [ {children: [], title: '2'} ], title: '1'}, {children: [ {children: [], title: '4'}, {children: [], title: '5'} ], title: '3'} ] console.log(flatten(test))
您的代码缺少 else 语句并且递归调用不正确(您一遍又一遍地传递相同的数组,而不是传递其项目)。
你的函数可以这样写:
function flatten() {
// variable number of arguments, each argument could be:
// - array
// array items are passed to flatten function as arguments and result is appended to flat array
// - anything else
// pushed to the flat array as-is
var flat = [],
i;
for (i = 0; i < arguments.length; i++) {
if (arguments[i] instanceof Array) {
flat = flat.concat(flatten.apply(null, arguments[i]));
} else {
flat.push(arguments[i]);
}
}
return flat;
}
// flatten([[[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]], [[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]]]);
// [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2]
现代但不是跨浏览器
function flatten(arr) {
return arr.flatMap(el => {
if(Array.isArray(el)) {
return flatten(el);
} else {
return el;
}
});
}
这是针对此问题的 Vanilla JavaScript 解决方案
var _items = {'keyOne': 'valueOne', 'keyTwo': 'valueTwo', 'keyThree': ['valueTree', {'keyFour': ['valueFour', 'valueFive']}]}; // another example // _items = ['valueOne', 'valueTwo', {'keyThree': ['valueTree', {'keyFour': ['valueFour', 'valueFive']}]}]; // another example /*_items = {"data": [{ "rating": "0", "title": "The Killing Kind", "author": "John Connolly", "type": "Book", "asin": "0340771224", "tags": "", "review": "i still haven't had time to read this one..." }, { "rating": "0", "title": "The Third Secret", "author": "Steve Berry", "type": "Book", "asin": "0340899263", "tags": "", "review": "need to find time to read this book" }]};*/ function flatten() { var results = [], arrayFlatten; arrayFlatten = function arrayFlattenClosure(items) { var key; for (key in items) { if ('object' === typeof items[key]) { arrayFlatten(items[key]); } else { results.push(items[key]); } } }; arrayFlatten(_items); return results; } console.log(flatten());
这是一个从模仿 lodash 的_.concat()
的荒谬中提取的递归减少实现
它可以采用任意数量的数组或非数组参数。 阵列可以是任何深度级别。 结果输出将是单个扁平值数组。
export const concat = (...arrays) => {
return flatten(arrays, []);
}
function flatten(array, initial = []) {
return array.reduce((acc, curr) => {
if(Array.isArray(curr)) {
acc = flatten(curr, acc);
} else {
acc.push(curr);
}
return acc;
}, initial);
}
它可以将任意数量的数组或非数组值作为输入。
来源:我是荒谬的作者
这是我的功能方法:
const deepFlatten = (array => (array, start = []) => array.reduce((acc, curr) => {
return Array.isArray(curr) ? deepFlatten(curr, acc) : [...acc, curr];
}, start))();
console.log(deepFlatten([[1,2,[3, 4, [5, [6]]]],7]));
在 JavaScript 中展平数组的递归方法如下。
function flatten(array) {
let flatArray = [];
for (let i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) {
flatArray.push(...flatten(array[i]));
} else {
flatArray.push(array[i]);
}
}
return flatArray;
}
let array = [[1, 2, 3], [[4, 5], 6, [7, 8, 9]]];
console.log(flatten(array));
// Output = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
let array2 = [1, 2, [3, [4, 5, 6]]];
console.log(flatten(array2));
// Output = [ 1, 2, 3, 4, 5, 6 ]
下面的函数将数组变平并保持每个项目的类型,而不是将它们更改为字符串。 如果您需要不只包含数字之类的数字的平面数组,这很有用。 它可以平整任何类型的阵列,没有副作用。
function flatten(arr) {
for (let i = 0; i < arr.length; i++) {
arr = arr.reduce((a, b) => a.concat(b),[])
}
return arr
}
console.log(flatten([1, 2, [3, [[4]]]]));
console.log(flatten([[], {}, ['A', [[4]]]]));
答案列表中的另一个答案,使用递归展平数组:
let arr = [1, 2, [3, 4, 5, [6, 7, [[8], 9, [10]], [11, 13]], 15], [16, [17]]]; let newArr = []; function steamRollAnArray(list) { for (let i = 0; i < list.length; i++) { if (Array.isArray(list[i])) { steamRollAnArray(list[i]); } else { newArr.push(list[i]); } } } steamRollAnArray(arr); console.log(newArr);
为简化起见,检查索引处的元素是否是数组本身,如果是,则将其传递给同一函数。 如果它不是数组,则将其推送到新数组。
这应该工作
function flatten() {
var flat = [
];
for (var i = 0; i < arguments.length; i++) {
flat = flat.concat(arguments[i]);
}
var removeIndex = [
];
for (var i = flat.length - 1; i >= 0; i--) {
if (flat[i] instanceof Array) {
flat = flat.concat(flatten(flat[i]));
removeIndex.push(i);
}
}
for (var i = 0; i < removeIndex.length; i++) {
flat.splice(removeIndex - i, 1);
}
return flat;
}
其他答案确实指出了 OP 代码故障的根源。 编写更具描述性的代码,问题实际上归结为“数组检测/-reduce/-concat-recursion”......
(function (Array, Object) {
//"use strict";
var
array_prototype = Array.prototype,
array_prototype_slice = array_prototype.slice,
expose_internal_class = Object.prototype.toString,
isArguments = function (type) {
return !!type && (/^\[object\s+Arguments\]$/).test(expose_internal_class.call(type));
},
isArray = function (type) {
return !!type && (/^\[object\s+Array\]$/).test(expose_internal_class.call(type));
},
array_from = ((typeof Array.from == "function") && Array.from) || function (listAlike) {
return array_prototype_slice.call(listAlike);
},
array_flatten = function flatten (list) {
list = (isArguments(list) && array_from(list)) || list;
if (isArray(list)) {
list = list.reduce(function (collector, elm) {
return collector.concat(flatten(elm));
}, []);
}
return list;
}
;
array_prototype.flatten = function () {
return array_flatten(this);
};
}(Array, Object));
从其他答案之一借用代码作为概念证明......
console.log([
[[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]],
[[[0, 1, 2], [0, 1, 2]], [[0, 1, 2], [0, 1, 2]]]
].flatten());
//[0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, ..., ..., ..., 0, 1, 2]
我希望你能有所不同。 一种结合了递归和“for循环”/高阶函数。 我想在没有 for 循环或高阶函数的情况下回答。
再次检查数组的第一个元素是一个数组。 如果是,请执行递归,直到到达最里面的数组。 然后推到结果。 我希望我以纯粹的递归方式接近它。
function flatten(arr, result = []) {
if(!arr.length) return result;
(Array.isArray(arr[0])) ? flatten(arr[0], result): result.push(arr[0]);
return flatten(arr.slice(1),result)
}
我认为问题在于您使用arguments
的方式。
因为你说当你传递一个嵌套数组时,它会导致“超出最大调用堆栈大小”错误。
因为arguments[0]
是指向您传递给flatten
函数的第一个参数的引用。 例如:
flatten([1,[2,[3]]]) // arguments[0] will always represents `[1,[2,[3]]]`
因此,您的代码最终会一次又一次地使用相同的参数调用flatten
。
为了解决这个问题,我认为最好使用named arguments
,而不是使用arguments
,它本质上不是“真正的数组”。
有几种方法可以做到这一点:
使用 flat 方法和 Infinity 关键字:
const flattened = arr.flat(Infinity);
您可以使用 reduce 和 concat 方法展平任何数组,如下所示:
function flatten(arr) { return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flatten(cur) : cur), []); };
阅读更多: https ://www.techiedelight.com/recursively-flatten-nested-array-javascript/
const nums = [1,2,[3,4,[5]]];
const chars = ['a',['b','c',['d',['e','f']]]];
const mixed = ['a',[3,6],'c',[1,5,['b',[2,'e']]]];
const flatten = (arr,res=[]) => res.concat(...arr.map((el) => (Array.isArray(el)) ? flatten(el) : el));
console.log(flatten(nums)); // [ 1, 2, 3, 4, 5 ]
console.log(flatten(chars)); // [ 'a', 'b', 'c', 'd', 'e', 'f' ]
console.log(flatten(mixed)); // [ 'a', 3, 6, 'c', 1, 5, 'b', 2, 'e' ]
这是细分:
arr.map((el) => ...)
(Array.isArray(el))
展平(el)
: 埃尔
res.concat(...arr.map((el) => (Array.isArray(el)) ? flatten(el) : el));
--> 扩展运算符将复制所有元素而不是数组本身,同时与“res”连接
var nestedArr = [1, 2, 3, [4, 5, [6, 7, [8, [9]]]], 10]; let finalArray = []; const getFlattenArray = (array) => { array.forEach(element => { if (Array.isArray(element)) { getFlattenArray(element) } else { finalArray.push(element) } }); } getFlattenArray(nestedArr);
function flatten(arr) {
const flat = [];
arr.forEach((item) => {
Array.isArray(item) ? flat.push(...flatten(item)) : flat.push(item);
});
return flat;
}
function flatten(arr) {
return arr.reduce((acc, curr) => {
if (Array.isArray(curr)) {
return [...acc, ...flatten(curr)];
} else {
return [...acc, curr];
}
}, []);
}
我觉得你很亲近。 问题之一是您使用相同的参数调用 flatten 函数。 我们可以使用扩展运算符 ( ...
) 来确保我们在 arguments[i] 内部的数组上调用 flatten,而不是重复相同的参数。
我们还需要做更多的调整,这样我们就不会将更多的项目推入我们的数组中
function flatten() { var flat = []; for (var i = 0; i < arguments.length; i++) { if (arguments[i] instanceof Array) { flat.push(...flatten(...arguments[i])); } else { flat.push(arguments[i]); } } return flat; } console.log(flatten([1,2,3,[4,5,6,[7,8,9]]],[10,11,12]));
function flatArray(input) {
if (input[0] === undefined) return [];
if (Array.isArray(input[0]))
return [...flatArray(input[0]), ...flatArray(input.slice(1))];
return [input[0], ...flatArray(input.slice(1))];
}
您应该为递归添加停止条件。
例如,如果 len (arguments[i]) ==0 返回
我已经在 stackoverflow 的this page中发布了我的数组展平的递归版本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.