JavaScript Array group and groupToMap
JavaScript Array group and groupToMap

JavaScript Array group and groupToMap

Published
Apr 24, 2023
Author
⚠️
Array.prototype.group 和 Array.prototype.groupToMap API 目前还在实验阶段。
 
JS 数组新方法 groupgroupToMap 方法可以对数组中的数据按照条件进行分组。该提议目前已经进入 Stage Three 阶段,很快将会成为标准。目前只有 safari 浏览器实现了该标准,查看浏览器兼容性

对数组内容进行分组

分组前先准备数据:
const inventory = [ { name: "asparagus", type: "vegetables", quantity: 5 }, { name: "bananas", type: "fruit", quantity: 0 }, { name: "goat", type: "meat", quantity: 23 }, { name: "cherries", type: "fruit", quantity: 5 }, { name: "fish", type: "meat", quantity: 22 }, ];
数组 item 有 nametypequantity 属性,将数据按照 type 属性进行分组:
const result = inventory.group(({ type }) => type); /* 结果是: { vegetables: [ { name: 'asparagus', type: 'vegetables', quantity: 5 }, ], fruit: [ { name: "bananas", type: "fruit", quantity: 0 }, { name: "cherries", type: "fruit", quantity: 5 } ], meat: [ { name: "goat", type: "meat", quantity: 23 }, { name: "fish", type: "meat", quantity: 22 } ] } */
可以像调用 mapreduce 方法那样去调用 group 方法,group 方法会根据传入回调函数的返回值对数据进行分组,可以看到数据被分成了 3 组。
如果要对与特定对象相关的信息进行分组的话,可以使用 groupToMap 方法,这样即使对象被修改,依然可以作为分组的键使用,例如:
const restock = { restock: true }; const sufficient = { restock: false }; const result = inventory.groupToMap(({ quantity }) => quantity < 6 ? restock : sufficient, ); console.log(result.get(restock)); // [{ name: "bananas", type: "fruit", quantity: 5 }]
这样我们将商品分为需要补货和不需要补货的两组。当 restock 被修改,仍然可以作为分组的键使用。
// 键被修改后仍可以使用 restock["fast"] = true; console.log(result.get(restock)); // [{ name: "bananas", type: "fruit", quantity: 5 }] // 不能使用新的键,即使它具有相同的结构! const restock2 = { restock: true }; console.log(result.get(restock2)); // undefined

groupgroupToMap 的区别

由于 Map 可以接受对象作为键值,当需要根据对象进行分组时,可以使用 groupToMap

自己实现

由于该 API 目前还没有得到广泛支持,我们可以通过 reduce 实现自己的 group 方法。
Array.prototype.myGroup = function(callback) { return this.reduce((prev, curr) => { return { ...prev, [callback(curr)]: [ ...(prev[callback(curr)] || []), curr, ], }; }, {}); }
上面的代码中,通过 reduce 方法初始化要返回的对象,然后迭代数组并通过 callback 获取正在处理的元素的 key,然后将 key 相同的元素放在同一个数组中,来试一试。
const result = inventory.myGroup(({ type }) => type); /* 结果是: { vegetables: [ { name: 'asparagus', type: 'vegetables', quantity: 5 }, ], fruit: [ { name: "bananas", type: "fruit", quantity: 0 }, { name: "cherries", type: "fruit", quantity: 5 } ], meat: [ { name: "goat", type: "meat", quantity: 23 }, { name: "fish", type: "meat", quantity: 22 } ] } */
当然这个实现相对粗糙,如果不想自己实现,可以使用 lodash.groupBy 或者 core-js 进行
polyfill。groupToMap 方法的话,只需要初始化 Map 就好了。

结论

虽然可以通过 reduce 实现数组分组功能,或者是第三方提供的工具函数,但是随着越来越多的原生方法的加入,可以减少手动实现或者第三方函数库的引入。原生方法的引入,不管是客户端或是服务端代码,都可以更方便的对数组数据进行分组,提高一些效率。