现在,我们有一个数组对象,里面的内容,如下:
const people = [
{ name: 'Jack', age: 20, sex: 'female' },
{ name: 'Emily', age: 22, sex: 'female' },
{ name: 'Michael', age: 23, sex: 'male' },
{ name: 'Taylor', age: 25, sex: 'female' },
{ name: 'Johnson', age: 37, sex: 'male' },
{ name: 'William', age: 22, sex: 'female' },
{ name: 'Elizabeth', age: 23, sex: 'male' },
{ name: 'Andrew', age: 25, sex: 'female' },
{ name: 'Wilson', age: 27, sex: 'female' },
{ name: 'Andrew', age: 25, sex: 'male' }
]
此时,我们有个需求:按照年龄分组,年龄作为对象的键名,值为分类的数组,此时,代码可以这样写
const result = {}
for (const item of people) {
const key = item.age
if (!result[key]) {
result[key] = []
}
result[key].push(item)
}
console.log(result)
// 打印的result结果为
{
"20": [
{"name": "Jack", "age": 20, "sex": "female"}
],
"22": [
{"name": "Emily", "age": 22, "sex": "female"},
{"name": "William", "age": 22, "sex": "female"}
],
"23": [
{"name": "Michael", "age": 23, "sex": "male"},
{"name": "Elizabeth", "age": 23, "sex": "male"}
],
"25": [
{"name": "Taylor", "age": 25, "sex": "female"},
{"name": "Andrew", "age": 25, "sex": "female"},
{"name": "Andrew", "age": 25, "sex": "male"}
],
"27": [
{"name": "Wilson", "age": 27, "sex": "female"}
],
"37": [
{"name": "Johnson", "age": 37, "sex": "male"}
]
}
如果我们要按照性别分组的话,那么上面的代码需要改为:
const result = {}
for (const item of people) {
const key = item.sex
if (!result[key]) {
result[key] = []
}
result[key].push(item)
}
console.log(result);
// 打印的result结果为
{
"female": [
{"name": "Jack","age": 20,"sex": "female"},
{"name": "Emily","age": 22,"sex": "female"},
{"name": "Taylor","age": 25,"sex": "female"},
{"name": "William","age": 22,"sex": "female"},
{"name": "Andrew","age": 25,"sex": "female"},
{"name": "Wilson","age": 27,"sex": "female"}
],
"male": [
{"name": "Michael","age": 23,"sex": "male"},
{"name": "Johnson","age": 37,"sex": "male"},
{"name": "Elizabeth","age": 23,"sex": "male"},
{"name": "Andrew","age": 25,"sex": "male"}
]
}
通过上面的两个例子,我们发现,如果需求在一直变化,那么我们的函数需要写很多个,这样很不符合我们开发的逻辑,那应该如何处理呢?通过分析,发现只需要写一个通用的分组函数来进行处理,此时方法可以进行修改了:
/**
* @arr: 数组
* @propName: 需要分组的字段
* */
function groupBy(arr, propName) {
const result = {}
for (const item of arr) {
const key = item[propName]
if (!result[key]) {
result[key] = []
}
result[key].push(item)
}
return result
}
console.log(groupBy(people, 'sex'))
console.log(groupBy(people, 'age'))
此时打印的结果和上述的一样,这样我们就得到了一个通用的分组函数。
现在,这个函数就是我们想要的吗?它是否能够真正满足我们任何的分组条件?接下来我们看,此时,如果数组对象中还有一个嵌套的对象:
const people = [
{ name: 'Jack', age: 20, sex: 'female' , address: {province: '河南省', city: '郑州市'}},
{ name: 'Emily', age: 22, sex: 'female', address: {province: '河南省', city: '开封市'}},
{ name: 'Michael', age: 23, sex: 'male', address: {province: '河南省', city: '郑州市'}},
{ name: 'Taylor', age: 25, sex: 'female', address: {province: '河南省', city: '南阳市市'}},
{ name: 'Johnson', age: 37, sex: 'male', address: {province: '陕西省', city: '安康市'}},
{ name: 'William', age: 22, sex: 'female', address: {province: '陕西省', city: '安康市'}},
{ name: 'Elizabeth', age: 23, sex: 'male', address: {province: '陕西省', city: '西安市'}},
{ name: 'Andrew', age: 25, sex: 'female', address: {province: '陕西省', city: '西安市'}},
{ name: 'Wilson', age: 27, sex: 'female', address: {province: '山东省', city: '临沂市'}},
{ name: 'Andrew', age: 25, sex: 'male', address: {province: '山东省', city: '青岛市'}}
]
更或者说,我们现在需要一个组合的分组条件,例如 "25-male": [{...}, {...}]
,亦是说,现在传入的根本就不是一个数组对象,就是一个简单的数组,例如:const arr = [2, 3, 5, 7, 11, 18, 21, 22]
, 分组的条件为根据数据的奇偶性来进行分组,那上面的 groupBy()
方法还能够支持吗? 很显然,不支持!!
但是,我们就是想要一个通用的分组函数,无论任何的数组类型,任何的分类条件,都可以满足,都能够实现我们想要的效果,那么我们就要对上面的方法进行改造了
通过观察 groupBy()
方法,可以发现决定数组是否能够按照自己想要的条件进行处理的是函数的 const key = item[propName]
,我们只需要对 item[propName]
进行改造就可以了
item[propName]
数据来源于函数中的参数 propName
, 此时的 propName
只是一个字符串,而我们的分组条件是不固定的,所以,propName
需要是一个方法。注意哈,改变后的参数会使 const key = ***
变为一个获取 key
值的过程,而不是直接获取值,我们来修改一下方法:
function groupBy(arr, generateKey) {
const result = {}
for (const item of arr) {
const key = generateKey(item)
if (!result[key]) {
result[key] = []
}
result[key].push(item)
}
return result
}
当然,方法改变后,我们的调用方式也需要进行修改,之前的函数第二个值直接可以传固定值,但是现在变为传一个方法,这个方法就是我们需要分组的条件:
// 按照 性别 分组
console.log(groupBy(people, (item) => item.sex))
// 按照 年纪 分组
console.log(groupBy(people, (item) => item.age))
// 按照 年纪-性别 分组
console.log(groupBy(people, (item) => `${item.age}-${item.sex}`))
// 按照 年纪-性别 分组 打印的结果
{
"20-female": [
{"name": "Jack","age": 20,"sex": "female"}
],
"22-female": [
{"name": "Emily","age": 22,"sex": "female"},
{"name": "William","age": 22,"sex": "female"}
],
"23-male": [
{"name": "Michael","age": 23,"sex": "male"},
{"name": "Elizabeth","age": 23,"sex": "male"}
],
"25-female": [
{"name": "Taylor","age": 25,"sex": "female"},
{"name": "Andrew","age": 25,"sex": "female"}
],
"37-male": [
{"name": "Johnson","age": 37,"sex": "male"}
],
"27-female": [
{"name": "Wilson","age": 27,"sex": "female"}
],
"25-male": [
{"name": "Andrew","age": 25,"sex": "male"}
]
}
现在,我们将出入的数组对象,更换为数字数组,要求按照奇偶数来进行分组
const arr = [2, 3, 5, 7, 11, 18, 21, 22]
console.log(groupBy(arr, (item) => (item % 2 === 0) ? '偶数' : '奇数'))
结果为:
{
"偶数": [2, 18, 22],
"奇数": [3, 5, 7, 11, 21]
}
按照上述的分组方法,虽然我们能够自定义了分组的条件,但是传值的类型始终受到限制,我们只能传方法,可是我们想要的是:传入的参数为方法,那么就按照定义的方法来分组,传的为值,那就按照传入的值来分组,很显然,当前的方法,并不支持,我们还需要进一步修改,那么应该如何修改呢?此时,我们应该会想到:“参数归一化”,看看前面的案例,如果我们传入的是一个值,例如:传入 sex
, 其实在方法内部执行的是key = item.sex
, 传入age
, 在方法内部执行的是key = item.age
, 无论我们传入的任何值,都会自动转换为方法,那按照这个思路,我们只需要将传入的值,或者方法,统一变为方法执行,那这个方法就会变得非常灵活了:
/**
* @arr: 数组
* @generateKey: 需要分组的字段||方法
* */
function groupBy(arr, generateKey) {
if (typeof generateKey === 'string') {
const propName = generateKey;
generateKey = (item) => item[propName]
}
const result = {}
for (const item of arr) {
const key = generateKey(item)
if (!result[key]) {
result[key] = []
}
result[key].push(item)
}
return result
}
希望此篇文章,通过小小的案例,能为大家打开思路,编程路畅通无比 ~ (๑╹◡╹)ノ"""’