好 pipe 不用嗎?(. ❛ ᴗ ❛.),讓程式碼更簡潔彈性吧!

更新於 發佈於 閱讀時間約 8 分鐘
raw-image
鱈魚的魚缸搬家了!新家文章有重新修訂,歡迎來新家看看喔。(´▽`ʃ♡ƪ)

甚麼是 pipe

顧名思是就是水管,但是不會像瑪利歐由人來鑽,而是讓資料通過。

pipe 是一種函數式程式設計中的概念,用來表示一條資料處理的管道。

可以將多個 function 組合在一起,資料會依序經過每個 function 進行處理。

這種方法使程式碼更加清晰、模組化,並且易於維護。

個人覺得和以往指令式的寫法最大差別在於關注點不同。

怎麼說呢?讓我們看看以下兩種等效的程式。

例 1:

const data = [1, 2, 3];
const filteredData = data.filter(value => value > 1);
const result = filteredData.join('-');

例 2:

const result = pipe(
[1, 2, 3],
filter(value => value > 1),
join('-'),
);
我知道你很想問例 1 明明可以一行解決。
先別計較那麼多,只是簡單範例啦。( •̀ ω •́ )✧


可以注意到例 1 的專注點在「結果」,而例 2 的寫法專注點在「過程」,了解差異後學習轉換上就會相對容易了。(應該啦 ¯\( ͡° ͜ʖ ͡°)/¯)

ramda、remeda?

這兩個都是基於 FP 概念設計的優秀套件,其實選一個喜歡的都行。

如果問我到底要用哪一個?實務上我 2 個都用。ヽ(✿゚ ▽゚)ノ

不過主要使用 remeda,remeda 缺少的 function 從 ramda 補。

為甚麼主要用 remeda 呢?因為 remeda 可以完美配合 TypeScript,而且同時支援 data-first 與 data-last 的寫法,兩個範例如下:

  • data-first:
const data = {
name: 'cod',
age: '18',
}

const result = pick(data, ['name']);
  • data-last:
const data = {
name: 'cod',
age: '18',
}

const pickName = pick(['name']);
const result = pickName(data);

個人覺得使用上更為直覺。

此外 remeda 還有其他特性,詳細說明就不再此贅述,可以參考以下文檔。

Remeda

所以 pipe 有甚麼好處?

個人覺得最大的好處是不用一直想變數名稱 XD,而且 function 方便抽換,整體來說增加了程式的彈性。

讓我們看看以下例子。

假設我們有多個 IoT 設備回傳資料,網頁需要彙整並顯示內容,資料為:

interface Datum {
deviceId: string;
type: string;
temperature: number;
humidity: number;
otherSensorData: {
type: string;
value: number;
}[];
}

const data: Datum[] = [
{
deviceId: 'device_1',
type: 'A',
temperature: 24.5,
humidity: 50.0,
otherSensorData: [
{ type: 'A', value: 10 },
{ type: 'A', value: 20 }
]
},
{
deviceId: 'device_2',
type: 'B',
temperature: 22.3,
humidity: 45.5,
otherSensorData: [
{ type: 'B', value: 15 },
{ type: 'C', value: 25 }
]
}
]

以下讓我們來實際撰寫程式。

列出所有設備 ID 並用頓號分隔

熟悉 JS 的人一定可以很快寫出以下程式:

const result01 = data
.map(({ deviceId }) => deviceId)
.join('、');

用 pipe 寫則會像這樣:

const result02 = pipe(
data,
map(prop('deviceId')),
join('、')
);

看起來好像沒比較好捏?但是用 remeda 可以更簡單抽離與複用:

const getDeviceIdListString = createPipe(
map<Datum, string>(prop('deviceId')),
join('、')
);

也方便加入新的處理邏輯:

import { trim } from 'ramda';

const getDeviceIdListString = createPipe(
/** 將每個 deviceId 去除頭尾空白 */
map<Datum, string>(
createPipe(prop('deviceId'), trim),
),
join('、'),
);

邏輯越複雜效果會越明顯,來看看其他例子。

將設備依照 type 分類

const groupByType = pipe(
data,
groupBy(prop('type')),
values,
);

取得平均溫度與平均濕度

const meanData = {
temperature: pipe(data, meanBy(prop('temperature'))),
humidity: pipe(data, meanBy(prop('humidity'))),
};

取得 otherSensorData type 種類清單

const typeList = pipe(
data,
/** 將所有 otherSensorData 攤平、組成新矩陣 */
flatMap(prop("otherSensorData")),
/** 依照 type 數值去除重複項目 */
uniqBy(prop("type")),
/** 取出 type 數值產生新矩陣 */
map(prop("type"))
);

取得所有溫溼度不在舒適範圍內的設備

function isComfortableTemperature(value: number) {
return value >= 22 && value <= 28;
}
function isComfortableHumidity(value: number) {
return value >= 40 && value <= 60;
}

const isComfortable = createPipe(
allPass<Datum>([
createPipe(prop('temperature'), isComfortableTemperature),
createPipe(prop('humidity'), isComfortableHumidity),
])
)

const uncomfortableList = pipe(data, reject(isComfortable))

/**
* 因為只有一個參數,所以也可以用 data-first 的方式寫
* const result = reject(data, isComfortable);
*/

從以上例子來看,其實就算沒有註解,從 function 的名稱我們看得出來此程式在做甚麼。

這樣可讀性是不是提升了許多呢?( •̀ ω •́ )✧


如果錯誤或更好的做法,歡迎大家多多指教。(´▽`)



留言
avatar-img
留言分享你的想法!
avatar-img
鱈魚的魚缸
17會員
14內容數
各種鱈魚滾鍵盤的雜紀
鱈魚的魚缸的其他內容
2024/07/16
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
2024/07/16
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
2024/06/28
ts-rest 可以實現從服務器到客戶端的全型別安全,可以有效降低前後端溝通血流成河的機率。(´,,•ω•,,)
Thumbnail
2024/06/28
ts-rest 可以實現從服務器到客戶端的全型別安全,可以有效降低前後端溝通血流成河的機率。(´,,•ω•,,)
Thumbnail
看更多
你可能也想看
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
  除了上篇提到的 Data Wrangler 外,SageMaker 還有許多配套的功能,其中有個叫做 Pipelines 的東西,說是可以用來構建、 管理及自動化深度學習流程,能夠節省人工操作,有那麼神?這次就來試試 Pipelines 能夠為我們帶來什麼體驗。
Thumbnail
  除了上篇提到的 Data Wrangler 外,SageMaker 還有許多配套的功能,其中有個叫做 Pipelines 的東西,說是可以用來構建、 管理及自動化深度學習流程,能夠節省人工操作,有那麼神?這次就來試試 Pipelines 能夠為我們帶來什麼體驗。
Thumbnail
Lazy evaluation 的效益必須是在 pipe 的組合上有最佳化過的,若組合的不好反而更糟糕,且在 I/O 上幫助似乎也不大。parallel stream 要能發揮效果必須看資料的來源類型,不過要注意的是 parallel stream 也會使記憶體的使用量增加,使用上也要小心。
Thumbnail
Lazy evaluation 的效益必須是在 pipe 的組合上有最佳化過的,若組合的不好反而更糟糕,且在 I/O 上幫助似乎也不大。parallel stream 要能發揮效果必須看資料的來源類型,不過要注意的是 parallel stream 也會使記憶體的使用量增加,使用上也要小心。
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
Thumbnail
老實說,看到 Java Sream API 讓我感到相當親切,這應該跟我研究所多年的研究題目是 visual dataflow language 有關,Java Stream API 把迴圈給內化了,每個 operation 的重點是要做什麼,大大提高了程式的抽象化程度和可讀性。
Thumbnail
老實說,看到 Java Sream API 讓我感到相當親切,這應該跟我研究所多年的研究題目是 visual dataflow language 有關,Java Stream API 把迴圈給內化了,每個 operation 的重點是要做什麼,大大提高了程式的抽象化程度和可讀性。
Thumbnail
pipe 代表函數式程式設計中的概念,利用多個功能結合在一起,資料依序通過每個功能進行處理。文章中介紹了 pipe 的優點、兩個等效的程式碼比較以及 remeda 套件的使用。詳細介紹了使用 pipe 的好處,並提供了多個相關的例子,展示了 pipe 可讀性的提升。
Thumbnail
pipe 代表函數式程式設計中的概念,利用多個功能結合在一起,資料依序通過每個功能進行處理。文章中介紹了 pipe 的優點、兩個等效的程式碼比較以及 remeda 套件的使用。詳細介紹了使用 pipe 的好處,並提供了多個相關的例子,展示了 pipe 可讀性的提升。
Thumbnail
如果我只是想要重複做一些很簡單的運算,還有沒有更簡潔的方式,那就是Lambda匿名函式。 本文將介紹 : Lambda匿名函式的用法,也比較跟自定函式的差異之處。 結合map,filter,sorted函式做應用介紹
Thumbnail
如果我只是想要重複做一些很簡單的運算,還有沒有更簡潔的方式,那就是Lambda匿名函式。 本文將介紹 : Lambda匿名函式的用法,也比較跟自定函式的差異之處。 結合map,filter,sorted函式做應用介紹
Thumbnail
本文將介紹自定函式及應用,利用程式範例解釋為什麼要用到自定函式 自定函式好處當然就是,讓你的程式碼看起來比較簡潔,在重複使用到的程式碼區塊,可以包裝成函式,讓你重複使用它。
Thumbnail
本文將介紹自定函式及應用,利用程式範例解釋為什麼要用到自定函式 自定函式好處當然就是,讓你的程式碼看起來比較簡潔,在重複使用到的程式碼區塊,可以包裝成函式,讓你重複使用它。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News