【问题标题】:Group by array and add field and sub array in main array按数组分组并在主数组中添加字段和子数组
【发布时间】:2018-03-21 00:48:19
【问题描述】:

我有一个像这样的重数组:

[
  {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中做到这一点?

【问题讨论】:

    标签: javascript arrays group-by


    【解决方案1】:

    这是我为这类情况写的一个 sn-p。您可以将此功能添加到所有数组中:

    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]};
        });
      }
    });
    

    你可以这样使用它。您可以只传递一个函数来定义您希望如何对数据进行分组。

    var newArray = arr.group(function (item) {
      return item.optionName;
    });
    

    Working Fiddle

    如果需要,可以将{key: k, data: map[k]}替换为{Name: k, Data: map[k]}


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

    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]}));
      }
    });
    

    像这样使用它:

    let newArray = arr.group(item => item.optionName))
    

    【讨论】:

      【解决方案2】:

      使用 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】:

        你可以使用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: [] }]);
        

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

        希望这会有所帮助。

        【讨论】:

          【解决方案4】:

          这是我为我在所有数组的应用程序功能中的情况编写的一个 sn-p。此 sn-p 代码用于节点 js 应用程序。以上都是给出的解决方案,但我在节点 js 的服务器端发现了一些问题。 这个sn-p是user full me....

            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');
          

          这个sn-p的代码输出结果.....

           [
          {"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

          【讨论】:

            【解决方案5】:
            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 });
            });
            

            【讨论】:

              【解决方案6】:
              '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);
              

              你可以试试这个方法。

              【讨论】:

                【解决方案7】:

                所有答案都会得出相同的结果,因此这一切都归结为个人偏好(或公司准则)如何解决这个问题。

                //  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
                

                虽然代码本身不同,但它与其他答案非常相似,只是所需步骤有所不同。

                人们也可以选择通过提取这些 aswel 来省略任何硬编码的选项名称(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)
                        });
                

                如果需要调整此代码,通过在简短的简洁步骤中编写所有数据处理步骤,您会帮自己(尤其是未来的自己)一个忙。

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

                【讨论】:

                • 很好的总结 Rogier,只需要更正一点:您提到了 ES6+ 中可用的解决方案,但数组原型过滤器、map 和 reduce 从 ES5.1 (2011) 开始可用,并受 IE9+、FF3+ 和所有 chrome 版本。
                • @AhmetCetin 我不完全确定我是否理解您的意思,在我的示例中,我提供了 ES5(.1) 语法以及用 ES6+ 编写的相同函数。也许我需要澄清第一个函数是 ES5(.1)?
                • 你是对的,我错过了,你在 ES6 中使用了粗箭头表示法。如果你提到其他语法对 ES5.1 有效,我想会更好地理解。当我第一次阅读时,我以为你的意思是所有对 ES6 有效的解决方案。
                • @AhmetCetin 感谢您的反馈,我想我现在说得更清楚了
                【解决方案8】:

                用法:

                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])
                }
                

                【讨论】:

                  猜你喜欢
                  • 2021-08-07
                  • 1970-01-01
                  • 2017-01-08
                  • 2019-05-10
                  • 2023-02-07
                  • 2020-11-30
                  相关资源
                  最近更新 更多