跟 Array push reduce 和 Object property mutation 說再見,JavaScript 優雅的處理資料結構技巧
資料結構的處理其實在前端還蠻常見的,但是常常因為大量使用迴圈 if else 等 imperative 語法而讓程式看起來複雜難懂,本文簡單介紹一些現代 JavaScript 中的一些技巧讓你可以優雅的處理資料結構
資料結構的處理其實在前端還蠻常見的,但是常常因為大量使用迴圈 if else 等 imperative 語法而讓程式看起來複雜難懂,本文簡單介紹一些現代 JavaScript 中的一些技巧讓你可以優雅的處理資料結構
conditional 在 Object 放入 property #
如何在某些條件下在一個 Object 中放入某個欄位,舉個例子,如果 itemList 的長度大於 0 就在 result object 放入 itemList,比較傳統的寫法如下
const data = {
user: "aaa",
};
const result = Object.assign({}, data);
if (itemList.length) {
result.itemList = itemList;
}
但是在 Object 裡面善用 spread operator 就可以不需要對 result 進行修改,宣告 result 的時候就可以一步到位
const data = {
user: "aaa",
};
const result = {
...data,
...(itemList.length > 0 && { itemList }),
};
conditional 在 Array 放入 item #
假設今天我們要根據使用者有沒有登入在 couponList 頂端放入會員專屬的 memberCoupons,傳統的寫法可以寫得像是這樣
const couponList = [];
if (isLogin) {
couponList.concat(memberCoupons);
}
couponList.concat(generalCoupons);
但是在 Array 裡面善用 spread operator,就可以在宣告 couponList 的時候一步到位處理掉
const couponList = [...(isLogin ? [memberCoupons] : []), ...generalCoupons];
要注意這邊 spread operator 在 Array 裡面後面一定要接 array,所以不能用 &&
取值,要改用 ? :
ternary operator , 另外在 Array 裡面也不能 spread Object
對 Object key value 做類似於 Array 的 map filter 處理 #
Array 裡面有方便 map filter 讓我們可以輕鬆的對 item 做操作,但是 Object 的資料結構裡面卻沒有,我們今天如果要把 data 裡頭的 value 濾掉 1000 以下並乘於 0.5 比較傳統的可以用 for in 這樣寫
const data = {
123321: 1400,
123322: 800,
123323: 1200,
};
const result = {};
for (let key in data) {
if (data[key] > 1000) {
result[key] = data[key] * 0.5;
}
}
如果你熟悉 reduce,喜歡遵守 immuatable 原則,可能會改寫成如下
const result = Object.keys(data).reduce(
(acc, key) => (data[key] > 1000 ? { ...acc, [key]: data[key] * 0.5 } : acc),
[]
);
雖然你沒有修改任何的 Object ,但是 spread object 的做法效能複雜度不是很好 O(n^2)
,reduce 也沒有辦法像是 map filter 容易理解
如果你知道 lodash 有類似 map filter 的 utils function,可以改寫如下
_.mapValues(
_.pickBy(data, (val) => val > 1000),
(val) => val * 0.5
);
但是一旦用到 lodash,就要小心的處理 bundle size 問題
ECMAScript 2017 Obejct.entries and ECMAScript 2019 Object.fromEntries
ECMAScript 最近幾年出了兩個可以解決這個問題的 api ,Object.entries 可以把 Object 轉成 [key, value] 的 tuple Array,Object.fromEntries 則可以把 [key, value] 的 tuple Array 轉成 Object,所以可以用這兩個 api 改寫
const result = Object.fromEntries(
Object.entries(data)
.filter(([key, value]) => value > 1000)
.map(([key, value]) => [key, value * 0.5])
);
Pipe Operator
另外 ECMAScript stage 1 proposal pipe operator 可以讓你避免巢狀嵌套的寫法,看起來又更漂亮了
const result =
data
|> Object.entries
|> (entries) => entries.filter(([key, value]) => value > 1000)
|> (entries) => entries.map(([key, value]) => value * 0.5);
|> Object.fromEntries
不過要小心這語法未來可能有兩種走向,投資有賺有賠,使用前請先詳看說明書 https://github.com/tc39/proposal-pipeline-operator