Javascript递归数组展平
Posted
技术标签:
【中文标题】Javascript递归数组展平【英文标题】:Javascript recursive array flattening 【发布时间】:2015-07-14 21:59:47 【问题描述】:我正在锻炼并尝试编写一个递归数组展平函数。代码在这里:
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;
问题是,如果我在那里传递一个数组或嵌套数组,我会收到“超出最大调用堆栈大小”错误。我做错了什么?
【问题讨论】:
旁注:***.com/questions/10865025/…flatten.apply(this, arguments[i]);
但这不是唯一的问题。
这能回答你的问题吗? Merge/flatten an array of arrays
【参考方案1】:
问题是你如何传递数组的处理,如果值是一个数组,那么你一直在调用它导致无限循环
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;
演示:Fiddle
这是一个更现代的版本:
function flatten(items)
const flat = [];
items.forEach(item =>
if (Array.isArray(item))
flat.push(...flatten(item));
else
flat.push(item);
);
return flat;
【讨论】:
这是另一个现代的:function flatten(array, accu = []) array.forEach(a => if(Array.isArray(a)) flatten(a, accu) else acc .push(a) ) 返回精度 为什么flat
的声明在flatten()
的递归调用中不屏蔽最外层的flat
变量?
@mrwnt10 我并不是要听起来讽刺,而是因为它实际上是最外层的flat
变量。每个连续帧中的底层flat
s 最终被返回,然后被推入顶层flat
最终返回。【参考方案2】:
在 2019 中使用 ES6 展平数组的简洁方法是 flat()
:
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]
Mozilla Docs
Can I Use - 21 年 12 月 94%
【讨论】:
【参考方案3】:如果项目是数组,我们只需将所有剩余的项目添加到这个数组中
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]]], []))
【讨论】:
这应该是选择的答案。它不使用任何迭代循环。【参考方案4】:[...arr.toString().split(",")]
使用Object
的toString()
方法。使用扩展运算符(...)
创建一个字符串数组,然后将其拆分为","
。
例子:
let arr =[["1","2"],[[[3]]]]; // output : ["1", "2", "3"]
【讨论】:
这会将数字转换为字符串,并将包含逗号的字符串分开。【参考方案5】:Haskellesque 方法...
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);
所以我认为上面的代码 sn-p 可以通过适当的缩进更容易理解;
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);
【讨论】:
【参考方案6】:如果你假设你的第一个参数是一个数组,你可以让它变得非常简单。
function flatten(a)
return a.reduce((flat, i) =>
if (Array.isArray(i))
return flat.concat(flatten(i));
return flat.concat(i);
, []);
如果您确实想要展平多个数组,只需在传递之前将它们连接起来。
【讨论】:
【参考方案7】:如果有人在寻找扁平化的对象数组(例如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))
【讨论】:
【参考方案8】:您的代码缺少 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]
【讨论】:
为什么flat
在flatten()
的递归调用中的声明不屏蔽外部flat
变量?
@mrwnt10(如果我正确理解您的问题)var flat
创建一个局部变量,因此它不会覆盖外部平面变量。【参考方案9】:
现代但不是跨浏览器
function flatten(arr)
return arr.flatMap(el =>
if(Array.isArray(el))
return flatten(el);
else
return el;
);
【讨论】:
【参考方案10】:这是针对这个问题的 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());
【讨论】:
【参考方案11】:这是一个从 absurdum 中提取的递归 reduce 实现,它模仿了 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);
它可以将任意数量的数组或非数组值作为输入。
来源:我是荒谬的作者
【讨论】:
【参考方案12】:这是我的函数式方法:
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]));
【讨论】:
回复较晚,但我很好奇您为什么要选择额外的 IIFE 包装器和start
参数。为什么不只是const deepFlatten = (xs) => xs .reduce ((a, x) => [...a, ... (Array .isArray (x) ? deepFlatten (x) : [x])], [])
?【参考方案13】:
在 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 ]
【讨论】:
【参考方案14】:这应该可以工作
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;
【讨论】:
【参考方案15】:其他答案确实指出了 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]
【讨论】:
【参考方案16】:我希望你能有所不同。一种结合了递归和“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)
【讨论】:
【参考方案17】:我认为问题在于您使用 arguments
的方式。
既然你说当你传递一个嵌套数组时,它会导致“超出最大调用堆栈大小”错误。
因为arguments[0]
是指向您传递给flatten
函数的第一个参数的引用。例如:
flatten([1,[2,[3]]]) // arguments[0] will always represents `[1,[2,[3]]]`
所以,您的代码最终会使用相同的参数一次又一次地调用 flatten
。
为了解决这个问题,我认为最好使用named arguments
,而不是使用arguments
,它本质上不是一个“真正的数组”。
【讨论】:
【参考方案18】:下面的函数扁平化数组并维护每个项目的类型,而不是将它们更改为字符串。如果您需要平面数组,它不仅包含像项目这样的数字,这很有用。它可以平整任何类型的阵列,没有副作用。
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]]]]));
【讨论】:
【参考方案19】:有几种方法可以做到这一点:
使用 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/
【讨论】:
【参考方案20】: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' ]
以下是细分:
-
用“map”循环遍历“arr”
arr.map((el) => ...)
-
在每次迭代中,我们将使用三元组来检查每个“el”是否为数组
(Array.isArray(el))
-
如果“el”是一个数组,则递归调用“flatten”并将“el”作为参数传入
展平(el)
-
如果“el”不是数组,则直接返回“el”
: 埃尔
-
最后,将三元的结果与“res”连接
res.concat(...arr.map((el) => (Array.isArray(el)) ? flatten(el) : el));
--> 扩展运算符将复制所有元素而不是数组本身,同时与“res”连接
【讨论】:
【参考方案21】: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);
在 finalArray 中,您将获得展平的数组
【讨论】:
请不要在几个现有问题中添加duplicate answers。【参考方案22】:使用 forEach 的解决方案
function flatten(arr)
const flat = [];
arr.forEach((item) =>
Array.isArray(item) ? flat.push(...flatten(item)) : flat.push(item);
);
return flat;
使用reduce的解决方案
function flatten(arr)
return arr.reduce((acc, curr) =>
if (Array.isArray(curr))
return [...acc, ...flatten(curr)];
else
return [...acc, curr];
, []);
【讨论】:
【参考方案23】:你应该为递归添加停止条件。
举个例子 if len (arguments[i]) ==0 返回
【讨论】:
为什么当 i==arguments.length 为真时它不停止? 那么,有什么问题?(【参考方案24】:我已在 *** 中的this page 发布了我的数组展平的递归版本。
【讨论】:
这是指向答案的链接,而不是答案。如果您要链接到与此问题相同的问题,请将此问题标记为与该问题重复。如果您觉得问题有所不同,请定制此问题的链接答案并将其发布在此处。以上是关于Javascript递归数组展平的主要内容,如果未能解决你的问题,请参考以下文章
javascript 通过递归来展平嵌套数组Ex:[1,2,3,[4,5,[7,8,[10,11,[12,13,[[[[[[14]]]]]]]]]] ]]