在JavaScript中扁平化嵌套数组

11

我有一个看起来非常糟糕的数组,它长这个样子:

编辑:

array = [
    {
        Letters: [{ Letter: 'A' }, { Letter: 'B' }, { Letter: 'C' }],
        Numbers: [{ Number: '1' }, { Number: '2' }, { Number: '3' }]
    },
    null,
    {
        Letters: [{ Letter: 'D' }, { Letter: 'E' }, { Letter: 'F' }, { Letter: 'G' }, { Letter: 'H' }],
        Numbers: [{ Number: '4' }, { Number: '5' }, { Number: '6' }, { Number: '7' }]
    }
];

并且希望数组看起来像这样:

flattenedArray = [a,b,c,1,2,3,d,e,f,g,h,4,5,6,7]

很遗憾,我无法更改原始格式,因为这是合并我正在获取的两个 API 响应时接收到的形式。

我已经尝试使用:

var flattenedArray = [].concat.apply([], array);

但它只是以输入格式呈现数组。

我想知道是否有人有任何建议?

编辑: 我尝试了给出的建议-非常感谢你们的帮助。看来问题在于列表的格式-不幸的是,使用Chrome控制台时,它以“树”格式显示,我无法直接查看数组输出的结构。

感谢你们所有人的帮助! 编辑2:请参见上面的实际数组,感谢您向我展示如何查看它!


1
这不是完全有效的JS数组语法。这只是你问题中的错误陈述,还是你实际上收到了一个看起来完全像这样的字符串? - Bergi
我不确定字符串的确切样子,这是我从 Chrome 开发者工具中解释得出的。我无法弄清如何将其呈现为非树形信息,但它看起来更像: - Jess
1
然后请向我们展示 console.log(JSON.stringify(array)) 生成的输出。得到树形视图表明您不是拥有一个普通字符串。 - Bergi
2
哦,好吧,那其实不仅仅是一个嵌套数组,而是一个嵌套对象的数组。这使得所有的答案都无效了 :-( - Bergi
1
@jess,现在你需要处理对象以及数组。你对null有什么要求? - Nina Scholz
显示剩余2条评论
10个回答

9
如果您拥有lodash,您可以使用以下代码:
_.flattenDeep(array)

如果您愿意,您还可以查看他们的源代码,以获取有关如何自行实现的想法。


9

针对嵌套数组/对象的新需求进行编辑,并实现扁平化,你可以采用结合测试元素类型的方法。

var array = [{ Letters: [{ Letter: 'A' }, { Letter: 'B' }, { Letter: 'C' }], Numbers: [{ Number: '1' }, { Number: '2' }, { Number: '3' }] }, null, { Letters: [{ Letter: 'D' }, { Letter: 'E' }, { Letter: 'F' }, { Letter: 'G' }, { Letter: 'H' }], Numbers: [{ Number: '4' }, { Number: '5' }, { Number: '6' }, { Number: '7' }] }],
    result = array.reduce(function iter(r, a) {
        if (a === null) {
            return r;
        }
        if (Array.isArray(a)) {
            return a.reduce(iter, r);
        }
        if (typeof a === 'object') {
            return Object.keys(a).map(k => a[k]).reduce(iter, r);
        }
        return r.concat(a);
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

旧请求和永恒的问题如何扁平化嵌套数组。

var flat = (r, a) => Array.isArray(a) ? a.reduce(flat, r) : r.concat(a),
    inputArray = array = [[['a', 'b', 'c'], [1, 2, 3]], [], [['d', 'e', 'f', 'g', 'h'], [4, 5, 6, 7]]],
    outputArray = inputArray.reduce(flat, []);

console.log(outputArray);


2
刚刚测试了一下,非常好用 - 谢谢你抽出时间来帮助! - Jess

8

您可以使用forEach()创建递归函数,它将返回新数组。

var array = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]]
function flat(data) {
  var r = []
  data.forEach(e => Array.isArray(e) ? r = r.concat(flat(e)) : r.push(e));
  return r;
}

console.log(flat(array))

您也可以使用reduce()代替forEach()

var array = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]]

function flat(data) {
  return data.reduce((r, e) => Array.isArray(e) ? r = r.concat(flat(e)) : r.push(e) && r, [])
}

console.log(flat(array))

正如@Bergi所建议的,您可以像这样使用reduce()

data.reduce((r, e) => r.concat(Array.isArray(e) ? flat(e) : [e]), [])

"&& r" 会降低可读性。我相信很多人不会理解它的目的。我建议将代码拆分成两行,并使用 "return r" 代替。 - Rajesh
应该使用reduce。如果需要命令式迭代,请使用for…of循环代替带箭头函数的forEach - Bergi
1
顺便提一下,在concat的情况下,您不需要重新分配r。最好只是使用(r, e) => r.concat(Array.isArray(e) ? flat(e) : [e])作为reducer。 - Bergi
@Bergi 哇,太棒了,只是为什么在 : [e] 上要加括号,而不是像这样 data.reduce((r, e) => r.concat(Array.isArray(e) ? flat(e) : e), []) - Nenad Vracar
因为只有数组应该传递给“concat”。普通值也有点用,但是concat始终需要检查它们是否可展开,就像数组一样(自ES6以来也不完全相同)。 - Bergi

2

对于这种情况,使用递归函数是很好的选择:

arr = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]];

function flatten(arr) {
    var result = [];
    for (var i = 0, len = arr.length; i < len; i++) {
       result = result.concat(Array.isArray(arr[i])? flatten(arr[i]) : [arr[i]]);
    }
    return result;
}
console.log(flatten(arr));


2
你可以尝试在Ramda中使用flatten函数。
  R.flatten([1, 2, [3, 4], 5, [6, [7, 8, [9, [10, 11], 12]]]]);
    //=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

你非常着迷于 Ramda。 - Abdennour TOUMI

1
你的数组格式不正确,你缺少了逗号(,)。这是正确的数组。 var array = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]];

var array = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]];
var result = flatten(array);
    
function flatten(array) {
    var flat = [];
    if(array !== undefined){
    var flat = [];
    for (var 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;
}

console.log(result);


我相信这段代码只能将一层嵌套的数组展开;如果需要展开更深层次的数组,你需要递归地运行此代码。或者像我建议的那样使用lodash库 ;) - Luan Nico

1
使用递归和展开运算符实现flatten函数。

const a = [1,[2,[3,4],[5]],6];
const flatten = (arr) => {
    const res = []
    for(let i=0;i<arr.length;i++) {
        if(!Array.isArray(arr[i])) res.push(arr[i]);
        else res.push(...flatten(arr[i]));
    }
    return res;
}
console.log(flatten(a));


1
没人想到原地拼接吗?
function flatten(array){
    for (var i = 0; i < array.length; i++) {
        if(array[i] instanceof Array){
            array.splice.apply(array,[i,1].concat(array[i]));
            i--;
        }
    };
    return array;
}

一次迭代,无递归。


0
function steamrollArray(arr) {
  var tmp = [];
  arr.forEach(function(val){
    if(Array.isArray(val))
      tmp = tmp.concat(steamrollArray(val));
    else
      tmp.push(val);
  });

  console.log(tmp);
  return tmp;
}

steamrollArray([1, [2], [3, [[4]]]]);

0
let arr = [1,2,[3,4]]

/* let newarr = arr.flat(); */

let newarr = Object.values(arr);
let arr2 = []
for(let val of Object.values(arr)) {
         if(!Array.isArray(val)){
     console.log(val)
     arr2.push(val)
     }    
           for ( let val2 of Object.values(val)){
             arr2.push(val2)
           }
}
console.log(arr2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接