繁体   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