[英]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.