按数组分组并在主数组中添加字段和子数组

时间:2015-12-30 04:42:31

标签: javascript arrays group-by

我有这样一个沉重的阵列:

[
  {Id: 1, Name: 'Red', optionName: 'Color'}, 
  {Id: 2, Name: 'Yellow', optionName: 'Color'},
  {Id: 3, Name: 'Blue', optionName: 'Color'},
  {Id: 4, Name: 'Green', optionName: 'Color'},
  {Id: 7, Name: 'Black', optionName: 'Color'},
  {Id: 8, Name: 'S', optionName: 'Size'},
  {Id: 11, Name: 'M', optionName: 'Size'},
  {Id: 12, Name: 'L', optionName: 'Size'},
  {Id: 13, Name: 'XL', optionName: 'Size'},
  {Id: 14, Name: 'XXL', optionName: 'Size'}
]

我需要做的是按optionName对它们进行分组,并在主数组中有两行,如下所示:

[
  {
    Name: 'Color',
    Data:[{Id: 1, Name: 'Red'},
          {Id: 2, Name: 'Yellow'},
          {Id: 3, Name: 'Blue'},
          {Id: 4, Name: 'Green'},
          {Id: 7, Name: 'Black'}]
  }, {
    Name: 'Size',
    Data:[{Id: 8, Name: 'S'},
          {Id: 11, Name: 'M'},
          {Id: 12, Name: 'L'},
          {Id: 13, Name: 'XL'},
          {Id: 14, Name: 'XXL'}]
  }
]

如何在javascript中完成?

8 个答案:

答案 0 :(得分:13)

这是我为这种情况写的一段摘录。您可以将此功能添加到所有阵列:

emit

你可以像这样使用它。您只需传递一个定义数据分组方式的函数即可。

Object.defineProperty(Array.prototype, 'group', {
  enumerable: false,
  value: function (key) {
    var map = {};
    this.forEach(function (e) {
      var k = key(e);
      map[k] = map[k] || [];
      map[k].push(e);
    });
    return Object.keys(map).map(function (k) {
      return {key: k, data: map[k]};
    });
  }
});

Working Fiddle

如果需要,可以将var newArray = arr.group(function (item) { return item.optionName; }); 替换为{key: k, data: map[k]}

这也是上面代码更紧凑的ES6版本:

{Name: k, Data: map[k]}

像这样使用:

Object.defineProperty(Array.prototype, 'group', {
  enumerable: false,
  value: function (key) {
    let map = {};
    this.map(e => ({k: key(e), d: e})).forEach(e => {
      map[e.k] = map[e.k] || [];
      map[e.k].push(e.d);
    });
    return Object.keys(map).map(k => ({key: k, data: map[k]}));
  }
});

答案 1 :(得分:3)

您可以使用reduce来获取所需的结果集:

var result = list.reduce(function(memo, item) {
  if (item.optionName === 'Color') {
    memo[0].Data.push(
      Id: item.Id,
      Name: item.Name
    });
  }
  if (item.optionName === 'Size') {
    memo[1].Data.push({
      Id: item.Id,
      Name: item.Name
    });
  }
  return memo;
}, [{ Name: 'Color', Data: [] }, { Name: 'Size', Data: [] }]);

变量列表是您的第一个列表。

希望这有帮助。

答案 2 :(得分:3)

使用Map对象的ES6解决方案:

function groupBy(arr, key) {
  	return arr.reduce(
      (sum, item) => {
      	const groupByVal = item[key];
        groupedItems = sum.get(groupByVal) || [];
        groupedItems.push(item);
      	return sum.set(groupByVal, groupedItems);
        },
      new Map()
      );
}

var Data = [ 
{ Id: 1, Name: 'Red', optionName: 'Color' }, 
  { Id: 2, Name: 'Yellow', optionName: 'Color' },
  { Id: 3, Name: 'Blue', optionName: 'Color' },
  { Id: 4, Name: 'Green', optionName: 'Color' },
  { Id: 7, Name: 'Black', optionName: 'Color' },
  { Id: 8, Name: 'S', optionName: 'Size' },
  { Id: 11, Name: 'M', optionName: 'Size' },
  { Id: 12, Name: 'L', optionName: 'Size' },
  { Id: 13, Name: 'XL', optionName: 'Size' },
  { Id: 14, Name: 'XXL', optionName: 'Size' } ];

document.getElementById("showArray").innerHTML =JSON.stringify([...groupBy(Data, 'optionName')], null, 4); 
<pre id="showArray"></pre>

答案 3 :(得分:1)

这是我在所有数组的应用程序功能中编写的一段代码。此代码段用于节点js应用程序。以上所有都给出了解决方案,但我在节点js的服务器端发现了一些问题。  这个片段是用户满满的......

  var Data= [ 
    { Id: 1, Name: 'Red', optionName: 'Color' }, 
      { Id: 2, Name: 'Yellow', optionName: 'Color' },
      { Id: 3, Name: 'Blue', optionName: 'Color' },
      { Id: 4, Name: 'Green', optionName: 'Color' },
      { Id: 7, Name: 'Black', optionName: 'Color' },
      { Id: 8, Name: 'S', optionName: 'Size' },
      { Id: 11, Name: 'M', optionName: 'Size' },
      { Id: 12, Name: 'L', optionName: 'Size' },
      { Id: 13, Name: 'XL', optionName: 'Size' },
      { Id: 14, Name: 'XXL', optionName: 'Size' } ];

    function groupBy(arr, key) {
                var newArr = [],
                    types = {},
                    newItem, i, j, cur;
                for (i = 0, j = arr.length; i < j; i++) {
                    cur = arr[i];
                    if (!(cur[key] in types)) {
                        types[cur[key]] = { type: cur[key], data: [] };
                        newArr.push(types[cur[key]]);
                    }
                    types[cur[key]].data.push(cur);
                }
                return newArr;
}

我这样使用它。我只是传递一个函数,它定义了如何对数据进行分组。

filterData= groupBy(Data,'optionName');

此代码输出片段的结果.....

 [
{"type":"Color","data":[{"Id":1,"Name":"Red","optionName":"Color"},
                       {"Id":2,"Name":"Yellow","optionName":"Color"},
                       {"Id":3,"Name":"Blue","optionName":"Color"}, 
                       {"Id":4,"Name":"Green","optionName":"Color"},
                       {"Id":7,"Name":"Black","optionName":"Color"}]},
{"type":"Size","data":[{"Id":8,"Name":"S","optionName":"Size"},
                       {"Id":11,"Name":"M","optionName":"Size"},
                       {"Id":12,"Name":"L","optionName":"Size"},
                       {"Id":13,"Name":"XL","optionName":"Size"},
                       {"Id":14,"Name":"XXL","optionName":"Size"}]}
]

Show on fiddle

答案 4 :(得分:0)

var originalList = [ { Id: 1, Name: 'Red', optionName: 'Color' }, 
  { Id: 2, Name: 'Yellow', optionName: 'Color' },
  { Id: 3, Name: 'Blue', optionName: 'Color' },
  { Id: 4, Name: 'Green', optionName: 'Color' },
  { Id: 7, Name: 'Black', optionName: 'Color' },
  { Id: 8, Name: 'S', optionName: 'Size' },
  { Id: 11, Name: 'M', optionName: 'Size' },
  { Id: 12, Name: 'L', optionName: 'Size' },
  { Id: 13, Name: 'XL', optionName: 'Size' },
  { Id: 14, Name: 'XXL', optionName: 'Size' } ];

var output = [{ Name: "Color", Data: [] },{ Name: "Size", Data: [] }] ;

originalList.map(function(entry){
    if ( entry.optionName === "Color") output[0].Data.push({ Id: entry.Id, Name: entry.Name });
    if ( entry.optionName === "Size") output[1].Data.push({ Id: entry.Id, Name: entry.Name });
});

答案 5 :(得分:0)

'use strict'

let l = [ { Id: 1, Name: 'Red', optionName: 'Color' }, 
          { Id: 2, Name: 'Yellow', optionName: 'Color' },
          { Id: 3, Name: 'Blue', optionName: 'Color' },
  { Id: 4, Name: 'Green', optionName: 'Color' },
  { Id: 7, Name: 'Black', optionName: 'Color' },
  { Id: 8, Name: 'S', optionName: 'Size' },
  { Id: 11, Name: 'M', optionName: 'Size' },
  { Id: 12, Name: 'L', optionName: 'Size' },
  { Id: 13, Name: 'XL', optionName: 'Size' },
  { Id: 14, Name: 'XXL', optionName: 'Size' } ];


let color = [];
let size  = [];

l.forEach(element => {
    if (element['optionName'] === 'Color') {
      color.push({'Id': element.Id, 'Name': element.Name});
    } else {
      size.push({'Id': element.Id, 'Name': element.Name});
    }
});  


console.log(color);
console.log(size);

你可以尝试这种方法。

答案 6 :(得分:0)

所有答案都会导致相同的结果,所以这一切都归结为个人偏好(或公司指导方针)如何解决这个问题。

//  ES5 (traditional javascript) version
function groupByOptionName(list, optionName) {
    return list
        //  filter out any item whose optionName does not match the desired name
        .filter(function(item) {
            return item.optionName === optionName;
        })
        //  map the item into the desired shape 
        //  (appears to be everything except optionName itself
        .map(function(item) {
            return {
                Id: item.Id,
                Name: item.Name
            };
        })
}

//  ES2015/ES6 version
function groupByOptionName(list, optionName) {
    return list
        //  filter out any item whose optionName does not match the desired name
        .filter(item => item.optionName === optionName)
        //  map the item into the desired shape 
        //  (appears to be everything except optionName itself
        .map(item => {
            Id: item.Id,
            Name: item.Name
        });
}

此功能可让您按如下方式编写所需的结果:

var output = [
    {Name: 'Color', Data: groupByOptionName(list, 'Color')},
    {Name: 'Size', Data: groupByOptionName(list, 'Size')},
];
//  the ES2015/ES6 version of this code would replace var with let

虽然代码本身不同,但它与其他答案非常相似,只需对所需步骤进行修改。

还可以选择通过提取所有硬编码选项名称(ColorSize),这样可以提供更动态的输入,但也可以引入更多实际需要的处理

//  ES5 (traditional javascript) version
function getOptionNames(list) {
    return list
        //  map the array into a list of optionNames
        .map(function(item) {
            return item.optionName;
        })
        //  remove duplicates
        .filter(function(item, index, all) {
            return all.indexOf(item) === index;
        });
}

//  ES2015/ES6 version (does exactly the same as the one above)
function getOptionNames(list) {
    return list
        //  map the array into a list of optionNames
        .map(item => item.optionName)
        //  remove duplicates
        .filter((item, index, all) => all.indexOf(item) === index);
}

这允许结果完全基于输入数据:

//  ES5 (traditional javascript) version
var output = getOptionNames(list)
        //  map the names into the desired structure
        .map(function(buffer, name) {
            return {
                Name: name,
                Data: groupByOptionName(list, name)
            };
        });

//  ES2015/ES6 version (does exactly the same as the one above)
var output = getOptionNames(list)
        //  map the names into the desired structure
        .map((buffer, name) => {
            Name: name,
            Data: groupByOptionName(list, name)
        });

通过简短的步骤编写所有数据修改步骤,如果需要调整此代码,您可以自己做(特别是未来的自己)。

如果数据集确实很重(就很多数据而言),您还必须确保保留在内存中的副本数量有限。例如,如果您从不需要原始数据集,请确保它可以被垃圾收集(通过在接收数据的范围之外没有包含它的变量)

答案 7 :(得分:0)

<强>用法:

groupValues([
    { color: 'blue', value: 100 }, 
    { color: 'blue', value: 75 }, 
    { color: 'yellow', value: 50 },
    { color: 'yellow', value: 25 }
], 'color')

<强>结果:

[
    [{ color: 'blue', value: 100 }, { color: 'blue', value: 75 }],
    [{ color: 'yellow', value: 50 }, { color: 'yellow', value: 25 }] 
]

<强>功能:

const groupValues = function(arr, key) {
    const mapped = {}
    arr.forEach(el => {
        const actualKey = el[key]
        if(!mapped.hasOwnProperty(actualKey)) mapped[actualKey] = []

        mapped[actualKey].push(el)
    })
    return Object.keys(mapped).map(el => mapped[el])
}