簡體   English   中英

Javascript遞歸數組展平

[英]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;
}

2019 年使用ES6扁平化數組的干凈方法是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]

Mozilla 文檔

我可以使用- 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(",")]

使用ObjecttoString()方法。 使用擴展運算符(...)創建一個字符串數組並用","分割它。

例子:

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 ,它本質上不是“真正的數組”。

有幾種方法可以做到這一點:

  1. 使用 flat 方法和 Infinity 關鍵字:

    const flattened = arr.flat(Infinity);

  2. 您可以使用 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' ]

這是細分:

  1. 用“map”循環“arr”

arr.map((el) => ...)

  1. 在每次迭代中,我們將使用一個三元來檢查每個“el”是否是一個數組

(Array.isArray(el))

  1. 如果 "el" 是一個數組,則遞歸調用 "flatten" 並傳入 "el" 作為其參數

展平(el)

  1. 如果 "el" 不是數組,則簡單地返回 "el"

: 埃爾

  1. 最后,將三元的結果與“res”連接起來

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);
在 finalArray 中,您將獲得展平的數組

使用 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];
    }
  }, []);
}

我覺得你很親近。 問題之一是您使用相同的參數調用 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM