【问题标题】:javascript | Object groupingjavascript |对象分组
【发布时间】:2014-03-13 15:19:52
【问题描述】:

我有一个对象。如下所示:

[
  {
    "name":"Display",
    "group":"Technical detals",
    "id":"60",
    "value":"4"
  },
  {
    "name":"Manufacturer",
    "group":"Manufacturer",
    "id":"58",
    "value":"Apple"
  },
  {
    "name":"OS",
    "group":"Technical detals",
    "id":"37",
    "value":"Apple iOS"
  }
]

我想按组字段对这些数据进行分组并得到这个对象:

var obj = {
    0 = [
    {
       'group'   = 'Technical detals',
       'name'    = 'Display',
       'id'      = '60',
       'value'   = '4'
    },
    {
       'group'   = 'Technical detals',
       'name'    = 'OS',
       'id'      = '37',
       'value'   = 'Apple iOS'
    }],
    1   = [
    {
       'group'   = 'Manufacturer',
       'name'    = 'Manufacturer',
       'id'      = '58',
       'value'   = 'Apple'
    }]
}

如何对我的第一个对象进行分组?

【问题讨论】:

  • 你真的想要一个对象字面量吗?既然你的索引是 0 和 1,那么数组不是更好吗?
  • 我认为有可能。

标签: javascript object merge grouping


【解决方案1】:

试试这样的:

function groupBy(collection, property) {
    var i = 0, val, index,
        values = [], result = [];
    for (; i < collection.length; i++) {
        val = collection[i][property];
        index = values.indexOf(val);
        if (index > -1)
            result[index].push(collection[i]);
        else {
            values.push(val);
            result.push([collection[i]]);
        }
    }
    return result;
}

var obj = groupBy(list, "group");

请记住,Array.prototype.indexOf 未在 IE8 及更早版本中定义,但有一些常见的 polyfills 用于此。

【讨论】:

  • 如何将 0、1 等更改为键名,如“组”
  • 我知道这是旧的,但我想一些人可能会偶然发现这一点,并希望必须将值分组作为键,使用相同的函数只需修改 if else 部分,如下所示:if (index &gt; -1) result[val].push(collection[i]); else { values.push(val); result[val] = []; result[val].push([collection[i]]); }
【解决方案2】:

如果您在应用程序中使用 underscore.js,那么您只需执行以下操作:

var groups = _.groupBy(data, 'group'); // data is your initial collection

或者如果你不想使用任何库,那么你可以自己做:

var groups = { };
data.forEach(function(item){
   var list = groups[item.group];

   if(list){
       list.push(item);
   } else{
      groups[item.group] = [item];
   }
});

你可以看到这两个例子http://jsfiddle.net/nkVu6/3/

【讨论】:

    【解决方案3】:

    您可以为组使用哈希表,并使用Array#forEach 来迭代数组。

    然后检查hash是否存在,如果不存在则分配一个空数组并push到结果集。

    稍后将实际元素推送到哈希数组。

    function groupBy(array, group) {
        var hash = Object.create(null),
            result = [];
    
        array.forEach(function (a) {
            if (!hash[a[group]]) {
                hash[a[group]] = [];
                result.push(hash[a[group]]);
            }
            hash[a[group]].push(a);
        });
        return result;
    }
    
    var data = [{ name: "Display", group: "Technical detals", id: 60, value: 4 }, { name: "Manufacturer", group: "Manufacturer", id: 58, value: "Apple" }, { name: "OS", group: "Technical detals", id: 37, value: "Apple iOS" }];
    	
    console.log(groupBy(data, "group"));
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • 你能在函数中以某种方式将其更改为降序吗/
    • @ApoloRadomer,降序是什么意思?哪个属性?
    • 当我分组时,我可以看到 JSON 排序 (ASC)。我想我必须使用反向使其下降。 @Nina Scholz
    【解决方案4】:

    如果你使用的是lodash,你可以使用groupBy

    它同时支持数组和对象。

    例子:

    _.groupBy([6.1, 4.2, 6.3], Math.floor);
    // => { '4': [4.2], '6': [6.1, 6.3] }
    
    // The `_.property` iteratee shorthand.
    _.groupBy(['one', 'two', 'three'], 'length');
    // => { '3': ['one', 'two'], '5': ['three'] }
    

    【讨论】:

      【解决方案5】:

      我尝试使用标记为已接受的答案,但注意到某些组中缺少元素,具体取决于所评估的属性类型。这是从该答案得出的解决方案:

      function groupBy(collection, property) {
        var i = 0, values = [], result = [];
        for (i; i < collection.length; i++) {
          if(values.indexOf(collection[i][property]) === -1) {
            values.push(collection[i][property]);
            result.push(collection.filter(function(v) { return v[property] === collection[i][property] }));
          }
        }
        return result;
      }
      var obj = groupBy(list, "group");
      

      【讨论】:

        【解决方案6】:

        如果你喜欢使用 ES6 Map,那么这是给你的:

        function groupBy(arr, prop) {
            const map = new Map(Array.from(arr, obj => [obj[prop], []]));
            arr.forEach(obj => map.get(obj[prop]).push(obj));
            return Array.from(map.values());
        }
        
        const data = [{ name: "Display", group: "Technical detals", id: 60, value: 4 }, { name: "Manufacturer", group: "Manufacturer", id: 58, value: "Apple" }, { name: "OS", group: "Technical detals", id: 37, value: "Apple iOS" }];
        	
        console.log(groupBy(data, "group"));
        .as-console-wrapper { max-height: 100% !important; top: 0; }

        Map 实例是根据输入数组生成的键/值对创建的。键是要分组的属性的值,值被初始化为空数组。

        然后填充这些数组。最后返回地图的值(即那些填充的数组)。

        【讨论】:

        • 谁能帮我解决这个问题:obj => [obj[prop], []]
        • @AnkitAgarwal,这是一个函数(箭头函数语法)。它接受一个参数 (obj) 并返回一个包含两个条目的数组。第一个条目的值为obj[prop](分组依据的属性值)。第二个条目是一个空数组。此函数作为回调参数传递给Array.from,它将为arr 中的每个对象调用它。 Map 构造函数可以处理这样一个小对数组,因此new Map 将为每个组获取一个键,并且每个组对应的值将是一个空数组。
        【解决方案7】:
        let g = (d,h={},r={},i=0)=>(d.map(x=>(y=x.group,h[y]?1:(h[y]=++i,r[h[y]-1]=[]),r[h[y]-1].push(x))),r);
        console.log( g(data) );
        

        let data=[
          {
            "name":"Display",
            "group":"Technical detals",
            "id":"60",
            "value":"4"
          },
          {
            "name":"Manufacturer",
            "group":"Manufacturer",
            "id":"58",
            "value":"Apple"
          },
          {
            "name":"OS",
            "group":"Technical detals",
            "id":"37",
            "value":"Apple iOS"
          }
        ];
        
        
        let g = (d,h={},r={},i=0)=>(d.map(x=>(y=x.group,h[y]?1:(h[y]=++i,r[h[y]-1]=[]),r[h[y]-1].push(x))),r);
        
        console.log(g(data));

        【讨论】:

        • 这是代码高尔夫:字节数最少的答案获胜。
        • 遗憾的是,这是完全不可读的,不应该在生产就绪代码中使用。对于任何将来要对此进行更改的人来说,它的可维护性将是非常糟糕的。
        【解决方案8】:

        Reduce 非常适合这种情况。鉴于list 以下是您的输入数据:

        const list = [{
            'name': 'Display',
            'group': 'Technical detals',
            'id': '60',
            'value': '4'
          },
          {
            'name': 'Manufacturer',
            'group': 'Manufacturer',
            'id': '58',
            'value': 'Apple'
          },
          {
            'name': 'OS',
            'group': 'Technical detals',
            'id': '37',
            'value': 'Apple iOS'
          }
        ];
        
        const groups = list.reduce((groups, item) => {
          const group = (groups[item.group] || []);
          group.push(item);
          groups[item.group] = group;
          return groups;
        }, {});
        
        console.log(groups);

        如果你想保持不变,你可以这样写reduce

        const list = [{
            'name': 'Display',
            'group': 'Technical detals',
            'id': '60',
            'value': '4'
          },
          {
            'name': 'Manufacturer',
            'group': 'Manufacturer',
            'id': '58',
            'value': 'Apple'
          },
          {
            'name': 'OS',
            'group': 'Technical detals',
            'id': '37',
            'value': 'Apple iOS'
          }
        ];
        
        const groups = list.reduce((groups, item) => ({
          ...groups,
          [item.group]: [...(groups[item.group] || []), item]
        }), {});
        
        console.log(groups);

        取决于您的环境是否允许展开语法。

        【讨论】:

          【解决方案9】:

          使用reducefilter

          假设您的初始数组分配给data

          data.reduce((acc, d) => {
              if (Object.keys(acc).includes(d.group)) return acc;
          
              acc[d.group] = data.filter(g => g.group === d.group); 
              return acc;
          }, {})
          

          这会给你类似的东西

          {
              "Technical detals" = [
              {
                 'group'   = 'Technical detals',
                 'name'    = 'Display',
                 'id'      = '60',
                 'value'   = '4'
              },
              {
                 'group'   = 'Technical detals',
                 'name'    = 'OS',
                 'id'      = '37',
                 'value'   = 'Apple iOS'
              }],
              "Manufacturer"   = [
              {
                 'group'   = 'Manufacturer',
                 'name'    = 'Manufacturer',
                 'id'      = '58',
                 'value'   = 'Apple'
              }]
          }
          

          【讨论】:

            【解决方案10】:

            有点不同,所以我们有一个简单的对象列表,并希望按属性对其进行分组,但包括所有相关的

            const data = [{'group':'1', 'name':'name1'},
            {'group':'2', 'name':'name2'},
            {'group':'2', 'name':'name3'},
            ,{'group':'1', 'name':'name4'}]; 
            
            const list = data.map( i => i.group);
            const uniqueList = Array.from(new Set(list));
            const groups= uniqueList.map( c => { 
                        return  { group:c, names:[]};
                    } ); 
            
            data.forEach( d => { 
                        groups.find( g => g.group === d.group).names.push(d.name);
            });
            

            所以结果会是这样的:

            [ {'group':'1', 'names':['name1', 'name4']},
            {'group':'2', 'names':['name2', 'name3']}
            

            相同,但使用 TypeScript 和 reduce:

            export function groupBy2 <T>( key: string, arr: T[]): { group: string, values: T[] }[] {
                 return arr.reduce((storage, item) => {
                     const a = storage.find(g => g.group === item[key]);
                     if (a) { a.values.push(item); }
                     else { storage.push({group: item[key], values: [item]}); }
                     return storage;
                 }, [] as {group: string, values: T[] }[]);
             }
            

            【讨论】:

              【解决方案11】:

              根据Anthony Awuley 的回答,我为 TypeScript 准备了通用解决方案!

              const groupBy = <T, K extends keyof T>(value: T[], key: K) =>
                value.reduce((acc, curr) => {
                  if (acc.get(curr[key])) return acc;
                  acc.set(curr[key], value.filter(elem => elem[key] === curr[key]));
                  return acc;
                }, new Map<T[K], T[]>());
              

              【讨论】:

                猜你喜欢
                • 2021-07-10
                • 2021-03-29
                • 2021-01-30
                • 2021-12-20
                • 2021-02-23
                • 1970-01-01
                • 2022-12-16
                • 1970-01-01
                相关资源
                最近更新 更多