按多个字段对每个对象进行分组

3

我正在尝试确定最适合对由属性定义的组进行排序的对象数组的数据类型。 我有一个对象数组,如下所示:

var people = [
    {
        name: 'Pete',
        gender: 'Male',
        age: 22

    },
    {
        name: 'Samantha',
        gender: 'Female',
        age: 20

    },
    {
        name: 'Frank',
        gender: 'Male',
        age: 22

    },
    {
        name: 'Gary',
        gender: 'Male',
        age: 21

    },
    {
        name: 'Maria',
        gender: 'Female',
        age: 20

    },
    {
        name: 'Hannah',
        gender: 'Female',
        age: 21

    },
    {
        name: 'Pete',
        gender: 'Male',
        age: 20

    }
];

我需要将这些对象分组到一个任意定义的组中。例如:
  1. 第一组:性别
  2. 第二组:年龄
(这可以由服务器定义,如果我们希望,可以更改为包含第三个组。)
然后给我展示的是(可视化):
Male:
   21:
     Gary
   22:
     Pete
     Frank

Female
   20:
     Samantha
     Maria
   21:
     Hannah

我认为适当的数据类型应该是一个对象中包含其他对象。例如:

{
    Male: {
        21: {
            [
                {
                    name: 'Gary',
                    gender: 'Male',
                    age: 21

                }
            ]
        },
        22: {
            [
                {
                    name: 'Pete',
                    gender: 'Male',
                    age: 22

                },
                {
                    name: 'Frank',
                    gender: 'Male',
                    age: 22

                }
            ]
        }
    },
    Female: {
        20: {
            [
                {
                    name: 'Samantha',
                    gender: 'Female',
                    age: 20

                },
                {
                    name: 'Maria',
                    gender: 'Female',
                    age: 20

                }
            ]
        },
        21: {
            [
                {
                    name: 'Hannah',
                    gender: 'Female',
                    age: 21

                }
            ]
        }
    }
}

但是,我却无法想出适合的算法,将这些对象排序为表示上述数据类型的数据。

underscore.js 中有一个有用的工具叫做 _.groupBy(arr, callback),可以按照以下方式使用:

_.groupBy(people, function (person) {
    var props = ['gender', 'age'], // server-defined
        prop = [];

    for (var i = 0, length = props.length; i < length; i++) {
        prop.push(person[props[i]]);
    }

    return prop.join('|');
});

这让我得到了一个一级深度的对象,可以使用for...in循环来迭代键,并循环构建上面的对象,但我卡在了代码的那一部分。
返回的对象将是:
{
    "Male|22": [
        {
            "name": "Pete",
            "gender": "Male",
            "age": 22
        },
        {
            "name": "Frank",
            "gender": "Male",
            "age": 22
        }
    ],
    "Female|20": [
        {
            "name": "Samantha",
            "gender": "Female",
            "age": 20
        },
        {
            "name": "Maria",
            "gender": "Female",
            "age": 20
        }
    ],
    "Male|21": [
        {
            "name": "Gary",
            "gender": "Male",
            "age": 21
        }
    ],
    "Female|21": [
        {
            "name": "Hannah",
            "gender": "Female",
            "age": 21
        }
    ],
    "Male|20": [
        {
            "name": "Pete",
            "gender": "Male",
            "age": 20
        }
    ]
}

我考虑循环遍历对象中的每个键,在管道符(|)处拆分,并使用递归构建一个新的对象,其中包含数据组/数组。

这是一种可怕的实现方式,但我不知道还有其他方法。

有没有更好的方法我可能没有想到?

5个回答

14
也许这可以帮助你。它利用一个包含对象属性的数组,将结果按属性内容分组。 forEach 循环遍历数据。 reduce 循环用于为每个给定组生成分组属性,如果它是最后一个,则返回一个数组,否则已经存在。
最后一步是将一个人的值推入数组中。
var people = [{ name: 'Pete', gender: 'Male', age: 22 }, { name: 'Samantha', gender: 'Female', age: 20 }, { name: 'Frank', gender: 'Male', age: 22 }, { name: 'Gary', gender: 'Male', age: 21 }, { name: 'Maria', gender: 'Female', age: 20 }, { name: 'Hannah', gender: 'Female', age: 21 }, { name: 'Pete', gender: 'Male', age: 20 }],
    groups = ['gender', 'age'],
    grouped = {};

people.forEach(function (a) {
    groups.reduce(function (o, g, i) {                            // take existing object,
        o[a[g]] = o[a[g]] || (i + 1 === groups.length ? [] : {}); // or generate new obj, or
        return o[a[g]];                                           // at last, then an array
    }, grouped).push(a);
});

document.write('<pre>' + JSON.stringify(grouped, 0, 4) + '</pre>');


这个有效!而且分组允许任意长度。:) 你能详细描述一下吗? - undefined
我之前有类似的东西,但是代码过于冗长,而我想要更简洁的解决方案 :) - 这太棒了! - undefined

5

您可以使用lodash。

groupBy(people, (item) => item.age + item.gender);

干净的解决方案。 - undefined

2

我真的不明白为什么人们总是使用框架去做那些事情。 Vanilla比框架更快,而且这并不太难编写...

var people = [    { name: 'Pete', gender: 'Male', age: 22 },     { name: 'Samantha', gender: 'Female', age: 20 },     { name: 'Frank', gender: 'Male', age: 22 },     { name: 'Gary', gender: 'Male', age: 21 },     { name: 'Maria', gender: 'Female', age: 20 },     { name: 'Hannah', gender: 'Female', age: 21 }, { name: 'Pete', gender: 'Male', age: 20 }];


var grouped = {}; // final object 
for (var i=0,len=people.length,p;i<len;i++) { // faster than .forEach
  p = people[i];

  if (grouped[p.gender] === undefined) // twice faster then hasOwnProperty
    grouped[p.gender] = {};

  if (grouped[p.gender][p.age] === undefined)
    grouped[p.gender][p.age] = [];

  grouped[p.gender][p.age].push(p); // your groupby is HERE xD
}
document.getElementById('grouped').innerHTML = JSON.stringify(grouped, null, 2)
<pre id="grouped"></pre>


1
我对你关于预期输出的看法持否定态度。我认为你应该创建一个带有MaleFemale数组属性的对象,并使用以下代码填充这些数组:
var obj = people.reduce(function (p, c, i) {
  p[c.gender].push(c); 
  return p;
}, { Male: [], Female: [] });

如果您想筛选这些数组,可以编写以下函数:
function getMaleByAge(obj, age) {
  return obj.Male.filter(function (el) {
    return el.age === age;
  })
}

getMaleByAge(obj, 21); // { name: "Gary", gender: "Male", age: 21 }

DEMO

的英译为:

{{链接1:演示}}


谢谢 @andy - 唯一的问题是,这个对象可能包含更多的属性(我使用这个示例来简化情况),而且分组可以是任意级别的。所以每个person对象可能包含,例如,20个属性,我们可能想将它们组织成5个组。明白吗? - undefined
1
然后根据您的需要对原始数据进行筛选。只要女性?people.filter(function (el) { return el.gender === 'Female' ; });而不是重新整理整个数据集,只需挑选出您需要处理的数据组。 - undefined
1
即使是21岁的女性:people.filter(function (el) { return el.gender === 'Female' && el.age === 21; });。简单吧。我觉得按照你计划的方式做会给自己增加很多工作量。希望无论如何都能成功 :) - undefined
谢谢Andy - 麻烦在于任意字段的列表(我还计划构建一个用户界面)。但是你的解决方案仍然很有帮助! - undefined

1
使用 _.groupBy_.map
var output = _.map(_.groupBy(people, 'gender'),function(obj, key){
  var temp = {};
  temp[key] = _.groupBy(obj, 'age')
  return temp;
});

console.log(JSON.stringify(output, null,' '))

将会输出以下内容。
 [{
  "Male": {
   "20": [
    {
     "name": "Pete",
     "gender": "Male",
     "age": 20
    }
   ],
   "21": [
    {
     "name": "Gary",
     "gender": "Male",
     "age": 21
    }
   ],
   "22": [
    {
     "name": "Pete",
     "gender": "Male",
     "age": 22
    },
    {
     "name": "Frank",
     "gender": "Male",
     "age": 22
    }
   ]
  }
 },
 {
  "Female": {
   "20": [
    {
     "name": "Samantha",
     "gender": "Female",
     "age": 20
    },
    {
     "name": "Maria",
     "gender": "Female",
     "age": 20
    }
   ],
   "21": [
    {
     "name": "Hannah",
     "gender": "Female",
     "age": 21
    }
   ]
  }
 }
]

这看起来非常令人兴奋 - 我现在就要实施这个(给我五)因为我有很多测试数据要尝试。 - undefined
这个能用在任意长度的分组属性上吗(比如说我有3个属性而不是2个)? - undefined
想一想,我可以使用递归...现在试试玩玩看。 - undefined
不用理会 - 我觉得我做不到。 - undefined

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