avatarHannah Lin

总结

本文介绍了 lodash 中的 FP(Functional Programming)函数 _.cond_.flow 的使用,并通过实战案例展示了如何在项目中应用这些函数来处理复杂的数据转换和逻辑判断。

摘要

文章首先提到了 lodash 是一个广泛使用的工具库,它在 4.17.15 版本中开始包含了 FP 模块,这使得开发者可以更容易地引入 FP 的实用函数。作者建议,即使不完全采用 FP 范式,也可以逐个函数开始尝试使用。文章接着介绍了 _.cond_.flow 这两个 FP 函数。_.cond 允许根据条件执行不同的函数,类似于 if-else 语句,而 _.flow 则可以按顺序执行一系列函数,实现函数组合。

文章随后展示了一个实战任务,该任务涉及到将不同图表的 API 数据转换为统一格式,并计算出现在与前一天的资料变动的文本描述。作者通过 _.cond_.flow 的组合使用,设计了一个通用的 getTrendText 函数,用于处理各种不同 shape 的数据输入,并输出标准化的结果。这个函数能够处理 Array 和 Object 类型的数据,并且能够适应多种数据源的变化。

最后,作者强调了使用 FP 在处理复杂数据和逻辑时的优势,尤其是在减少重复代码、提高代码可维护性和可调试性方面。通过 FP 的方法,代码变得更加简洁和模块化。

观点

  • FP 在处理数据转换和逻辑判断时表现出色:通过使用 _.cond_.flow,可以将复杂的逻辑分解成简单的函数,使得代码更加清晰和易于维护。
  • 数据标准化对于处理多种数据源至关重要:在实战案例中,作者通过将所有数据转换为 [number, number] 的格式,简化了后续的处理逻辑。
  • FP 能够提高代码的可重用性和灵活性:通过创建可组合的纯函数,可以在不同的上下文中重用这些函数,而不需要重写相似的逻辑。
  • FP 有助于减少 if-else 的使用:使用 _.cond 可以避免编写冗长的条件判断语句,使得代码更加精简。
  • FP 的代码更易于调试:由于函数的纯度和无副作用的特性,FP 风格的代码更容易定位和解决问题。

lodash 裡的 FP 函式實在太好用啦 — 實戰篇

介紹如何在實戰中使用 FP函式 _.cond、_.flow

lodash 擁有非常高的使用率,很多人也都喜歡它簡單好用的 utils !從 lodash 4.17.15 版開始,lodash/fp 也包含在裡面了(終於不需要額外 install) ,所以若想嘗試導入好用的 FP utils 完全可以無痛轉換啊 (使用 Function Programming可以從任何一個 function 著手,沒有規定一整個專案都要是 FP)。

若不知道什麼是 Functional Programming (FP) 的人,看這篇可能會頗吃力。我在去年鐵人賽寫了一系列文章,可以慢慢閱讀。

此篇會先介紹一下 _.cond_.flow 這兩個函示,但重點還是會放在如何在實際專案上運用。

好用的 lodash/FP utils

這邊只會簡單介紹一下,並會用在之後實戰中

_.flow([funcs])

把需要執行的 func 按照順序執行

_.flow 其實就是 Functional Programming 裡的 pipe [延伸閱讀: Compose vs. Pipe]

左: 一般人寫的 function / 右: FP flow 的概念圖
let add = (x, y) => x + y; 
let square = n => n*n;
const transform = _.flow([ add, square ]);
transform(1, 3); // 16

_.cond([ [ func1, func2 ] ])

func1 若回傳 true 那就執行 func2

const func = _.cond([ 
  [() => true, () => 'hi']
])
func(); // hi

_.cond厲害的是可以有很多個 condition

const func = _.cond([ 
  [(x) => x > 0, () => '大於零'],
  [(x) => x < 0, () => '小於零'],
  [() => true, () => '其他'] // Default 
])
func(5); // '大於零'
func('haha'); // '其他'

這邊很清楚可以看到三個判斷式,最後一個 () => true 永遠都會回傳 true,也就是 _.cond 的 default setting,若不符合 (x) => x > 0(x) => x < 0 就會執行 default

直接來看實戰 Task

畫面有許多不同圖表,每一個圖表都有相對應的 API,不同 widget API 間 shape 也差異很大。但可以看到每一個圖表都會顯示現在與前一天的資料的變動

我們就先把這個變動的 feature 叫做 getTrendText,程式面來說他會是一個函式並能輸出 以下四種結果

getTrendText
const getTrendText = (data = []) => {}
getTrendText([1, 2]); // ↑ 1
getTrendText([2, 1]); // ↓ 1
getTrendText([1, 1]); // No Change
getTrendText([-1, -1]); // N/A

這題難的不是 UI 顯示,而是 API Shape 有很多種。不旦有可能是 Array 、Object,就算是同型別但 shape 也可能不同

我有把這四個圖表的四種可能都完整寫在 mock-data.js 裡,可以點進去看唷

若輸入 data 都一樣,那隨便一個新手前端都能解。但難就難在輸入 data 實在太多可能性,所以重點就會被放在如何轉換資料這件事。

👎 以前的我

可能會分成四個 widgets,然後分別處理 getTrendText。但四個 widgets 裡就會有許多看起來很像但又不同的 code 例如以下

這樣寫起來既不好維護又不 clean code,更好作法就是把 getTrendText拉成一個客製化 utils 統一管理也較好維護。問題來了,四種 data 來源那是不是要寫超多 if else 先判斷是哪一種然後再個別處理?

👍 現在的我

寫了一年的 FP 後發現轉換資料這件事真的很適合用 FP 來解決,尤其是當你程式碼越寫越多、if else condition 隨處可見時,就是 FP 該出馬時了!

我們要先來把問題最小化及分解

  • 先想 Data 的 edge case,例如沒資料會顯示 N/A
  • 把 Data 轉換成一樣格式,這樣之後處理會簡單很多
  • 判斷 ↓ Decrease、↑ Increase、沒變、 N/A

先想 Data 的 edge case

Data 不是 Array 就是 Object,但若沒資料代表要 return N/A

{} or [] // 沒資料進來

當然你可以自己寫一個 function 判斷,但既然都用 lodash 也可以用 _.isEmpty

把 Data 轉換成一樣格式 [number, number]

我們的 data 可以簡化成以下,不但把四種來源差異很大的資料變成同格式,我們也只需要專注在最新的兩筆資料 (也就是今天及昨天的資料)

要怎麼做到以上呢? 需要幾個步驟

  1. angularGaugeChartData 變動較大,要處理成 [number, number] 格式

自己覺得 _.cond 用起來爽度真的大增啊!

2. 所有 Data 現在都是 Array 了,只需要拿最新的兩個 value,可以用 _.takeRight(2) 取得

3. 再來處理 lineChartData,也是處理成 [number, number] 格式

const handleLineData = _.flow([
  _.cond([
    [_.has('[0].value'), _.map(_.get('value'))],
    [_.has('[0].count'), _.map(_.get('count'))],
    [() => true, x => x]
  ]),
  _.map(Number)
]);

判斷差異多少 ↓ Decrease、↑ Increase、沒變

現在所有 data 都變成 [number, number] 格式,所以可以用以下運算輕易得到結果。

([x, y]) => y - x])

以上步驟全連在一起

這樣其實就完成 80%,最後只要針對 cacDelta 的結果 return 字串就好

完整 code 在這,可以玩玩看!

小結

個人認為 FP 很適合用在複雜的運算上面,雖然乍看程式碼並沒有比較少,但認真說真的蠻容易維護跟變更的,debug 起來也是容易許多。

Functional Programming
Fp
JavaScript
React
Recommended from ReadMedium