使用 JavaScript 進行數(shù)據(jù)分組最優(yōu)雅的方式
今天我們一起來看一個數(shù)據(jù)分組的小技巧。
對數(shù)據(jù)進行分組,是我們在開發(fā)中經(jīng)常會遇到的需求,使用 JavaScript 進行數(shù)據(jù)分組的方式也有很多種,但是由于沒有原生方法的支持,我們自己實現(xiàn)的數(shù)據(jù)分組函數(shù)通常都比較冗長而且難以理解。
不過,告訴大家一個好消息,一個專門用來做數(shù)據(jù)分組的提案 Array.prototype.groupBy 已經(jīng)到達 Stage 3 啦!

在看這個提案,之前,我們先來回顧下我們以前在 JavaScript 里是怎么分組的。
以前的方式
假設(shè)我們有下面一組數(shù)據(jù):
const?items?=?[
??{
????type:?'clothes',
????value:?'??',
??},
??{
????type:?'clothes',
????value:?'??',
??},
??{
????type:?'clothes',
????value:?'??',
??},
??{
????type:?'animal',
????value:?'??',
??},
??{
????type:?'animal',
????value:?'??',
??},
??{
????type:?'animal',
????value:?'??',
??},
];
我們希望按照 type 分組成下面的格式:
const?items?=?{
??clothes:?[
????{
??????type:?'clothes',
??????value:?'??',
????},
????{
??????type:?'clothes',
??????value:?'??',
????},
????{
??????type:?'clothes',
??????value:?'??',
????},
??],
??animal:?[
????{
??????type:?'animal',
??????value:?'??',
????},
????{
??????type:?'animal',
??????value:?'??',
????},
????{
??????type:?'animal',
??????value:?'??',
????},
??],
};
我們可能會用到下面的寫法:
for 循環(huán)
最直接而且容易理解的方法,就是代碼有點多。
const?groupedBy?=?{};
for?(const?item?of?items)?{
??if?(groupedBy[item.type])?{
????groupedBy[item.type].push(item);
??}?else?{
????groupedBy[item.type]?=?[item];
??}
}
reduce
使用 Array.protoype.reduce 雖然語法看起來簡單,但是太難讀了。
items.reduce(
??(acc,?item)?=>?({
????...acc,
????[item.type]:?[...(acc[item.type]????[]),?item],
??}),
??{},
);
我們稍微改造的容易理解一點,語法就跟上面的 for 循環(huán)差不多了:
items.reduce((acc,?item)?=>?{
??if?(acc[item.type])?{
????acc[item.type].push(item);
??}?else?{
????acc[item.type]?=?[item];
??}
??return?acc;
},?{});
filter
使用 Array.prototype.filter,代碼看起來很容易閱讀,但是性能很差,你需要對數(shù)組進行多次過濾,而且如果 type 屬性值比較多的情況下,還需要做更多的 filter 操作。
const?groupedBy?=?{
??fruit:?items.filter((item)?=>?item.type?===?'clothes'),
??vegetable:?items.filter((item)?=>?item.type?===?'animal'),
};
其他
如果你既不想用 reduce,還想用到函數(shù)式寫法,你可能會寫出下面的代碼:
Object.fromEntries(
??Array.from(new?Set(items.map(({?type?})?=>?type))).map((type)?=>?[
????type,
????items.filter((item)?=>?item.type?===?type),
??]),
);
是不是很讓人崩潰 ??~
Array.prototype.groupBy
好了,如果使用 Array.prototype.groupBy,你只需要下面這一行代碼:
items.groupBy(({?type?})?=>?type);
groupBy 的回調(diào)中一共有三個參數(shù):
參數(shù)1:數(shù)組遍歷到的當前對象 參數(shù)2:index 索引 參數(shù)3:原數(shù)組
const?array?=?[1,?2,?3,?4,?5];
//?groupBy?groups?items?by?arbitrary?key.
//?In?this?case,?we're?grouping?by?even/odd?keys
array.groupBy((num,?index,?array)?=>?{
??return?num?%?2?===?0???'even':?'odd';
});
另外,你還可以用 groupByToMap,將數(shù)據(jù)分組為一個 Map 對象。
//?groupByToMap?returns?items?in?a?Map,?and?is?useful?for?grouping?using
//?an?object?key.
const?odd??=?{?odd:?true?};
const?even?=?{?even:?true?};
array.groupByToMap((num,?index,?array)?=>?{
??return?num?%?2?===?0???even:?odd;
});
//?=>??Map?{?{odd:?true}:?[1,?3,?5],?{even:?true}:?[2,?4]?}
參考:
https://github.com/tc39/proposal-array-grouping https://www.charpeni.com/blog/array-prototype-group-by-to-the-rescue
評論
圖片
表情
