映射后连接嵌套数组

14

我有一个数组,其中包含多个类别对象,每个对象都有一个items属性,其中包含一组项目对象。 我想将每个类别中的每个项目映射到具有值和标签属性的对象[]数组中。由于某种原因,我无法执行连接操作。

var categories = [{
                name: "category1",
                items: [{
                    itemId: 1,
                    name: "Item1"
                }, {
                    itemId: 2,
                    name: "Item2"
                }]
            }, {
                name: "category2",
                items: [{
                    itemId: 3,
                    name: "Item3"
                }, {
                    itemId: 4,
                    name: "Item4"
                }]
            }];

var items = [];
for(var i = 0; i < categories.length; i++){
    items.concat($.map(categories[i].items,function(elem){
        return {value:elem.itemId, label:elem.name};
    }));
} 
console.log(items); //prints []

预期结果

[{
   label: "Item1",
   value: "1"
},
{
   label: "Item2",
   value: "2"
},{
   label: "Item3",
   value: "3"
},{
   label: "Item4",
   value: "4"
}

我觉得我似乎缺少了某个非常基础的东西。我记录了$.map函数的结果,它似乎返回了一个[]。有人能找出问题吗?

JSFiddle:http://jsfiddle.net/vymJv/


请忽略。我需要将项目分配给concat()的结果。 - Kevin Bowersox
6个回答

28

使用纯JavaScript的另一种方法:

var x = categories.map(function(val) {
  return val.items;
}).reduce(function(pre, cur) {
   return pre.concat(cur);
}).map(function(e,i) {
  return {label:e.name,value:e.itemId};
});

输出: x = [{label: "Item1", value: 1}, {label: "Item2", value: 2}, …]


以下是该段代码的ES6+简化版本:const x = categories.flatMap(val => val.items).map(({name: label, itemId: value}) => ({label, value})); - K. Waite
谢谢,兄弟,帮了大忙! - Vikas chhabra

12

使用flatMap进行更新(不兼容IE)

categories.flatMap((categories) => categories.items)

flatMap() 方法通过对数组的每个元素应用给定的回调函数,然后将结果降为一级形成新的数组。


2
这个解决了问题,不确定为什么这个答案不是最佳答案。 - Robert

10

concat() 方法用于连接两个或多个数组。

该方法不会改变现有的数组,而是返回一个新数组,包含连接的数组的值。

http://jsfiddle.net/vymJv/1/

for(var i = 0; i < categories.length; i++){
    items = items.concat($.map(categories[i].items, function(elem) {
        return {value: elem.itemId, label: elem.name};
    }));
}

谢谢你的回答,我发帖后不久就意识到了这一点。我想我需要把它表述清楚。我会在几分钟内接受答案。 - Kevin Bowersox

4
这段代码使用函数式编程方法解决了您的任务:
var items = [].concat.apply([], categories.map(cat =>
  cat.items.map(elem => ({ value:elem.itemId, label:elem.name })))
)

解释:Function.prototype.apply()的语法为fun.apply(thisArg, [argsArray]),它允许我们以数组形式提供函数参数。Array.prototype.concat()将任意数量的参数合并成一个数组。如果我们现在写Array.prototype.concat.apply([], [category1Items, ..., categoryNItems]),实际上等同于[].concat(category1Items, ..., categoryNItems),这将所有参数连接起来。您还可以用[].concat代替Array.prototype.concat使其更短。否则,我们只需使用标准映射完成工作。

为了清晰起见,您还可以将代码分开:

function getItem(elem){
  return {value:elem.itemId, label:elem.name};
}

function getCategoryItems(cat) {
  return cat.items.map(getItem);
}

function flatten(arr) {
  return Array.prototype.concat.apply([], arr);
}

var items = flatten(categories.map(getCategoryItems));

1
不确定语法是何时定义的,但现在[].concat(...arr)应该可以实现与您的“flatten”定义相同的功能。 - Silly Freak

4
  const items = categories
          .map(category => category.items)
          .reduce((prev, current) => [...prev, ...current])
          .map((e, i) => ({ label: e.name, value: e.itemId }));

5
仅包含代码的答案通常可以通过添加一些如何和为什么的解释来改善。 - Jason Aller

3
我们可以通过创建concatAll来扩展数组原型,它将通过迭代每个子数组,将每个值倒入新数组中,从而连接所有的数组。
Array.prototype.concatAll = function() {
  var results = [];
  this.forEach(function(subArray) {
    subArray.forEach(function(subArrayValue) {
      results.push(subArrayValue);
    });
  });
  return results;
};

然后,我们可以通过以下方式得到所需的结果:

let items = categories.map(function(category) {
  return category.items.map(function(item) {
    return {label: item.name, value: item.itemId};
  });
}).concatAll();

我们通过将每个类别转换为项目数组来获取项目。因为我们有两个类别,所以最终会得到两个项目数组。通过在最终结果上应用concatAll,我们可以展开这两个项目数组并获得所需的输出。

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