Skip to main content

Command Palette

Search for a command to run...

List 操作小技巧

Published
1 min read

最近寫 Elixir 時,有個慣用的 functional 小手法,順手記錄一下。

1. 無差別包裝 List

當處理的對象可能是 nillist,或是單個元素時,可以直接將它們包裝成單層的 list 進行後續操作。

Elixir 提供了 List.wrap 讓你直接將 nil 及 單個元素包裝成 list。對於原本就是 list 的對象則直接回傳:

# Elixir
List.wrap(nil)    # => []
List.wrap(1)      # => [1]
List.wrap([2, 3]) # => [2, 3]

值得一提的是包裝 nil 會回傳空的 list,這對稍後要介紹的連續技起了重要的作用。

Ruby 則可以用 Array()

# Ruby
Array(nil)    # => []
Array(1)      # => [1]
Array([2, 3]) # => [2, 3]

在 JavaScript 裡就得要自己拼了,以 Ramda 為例:

let wrap = R.compose(R.filter(R.identity), R.unnest, R.of)

wrap(null)    // => []
wrap(1)       // => [1]
wrap([2, 3])  // => [2, 3]

這裡用 R.unnest 而非 R.flatten,是因為它不會把串列全部攤平至只剩一層。這個特性在你想要處理的「元素」本身就是一個陣列時會排上用場。

2. 攤開 map 的結果。

flat_map 可以將 map 的結果攤開到外層串列中。這個看範例會比較好懂:

[1, 2, 3]
|> Enum.flat_map(fn x -> [x, x] end)

# => [1, 1, 2, 2, 3, 3]

flat_map 是函數式語言/函式庫基本的操作之一,應該都找得到。Ramda 裡叫 R.chain

3. 連續技:優雅的消失

結合上面兩個函式,先把含有 nil 的集合 wrap 成一個個的串列,再用 flat_mapflatten 攤開。這個時候就會發現 nil 在操作過程中直接消失了,不需要 filter,更不用 if/else 判斷。例如:

class = %{
  teacher: %{name: "John", age: 40},
  assistant: nil,
  students: [%{name: "Amber", age: 20}, %{name: "William", age: 22}]
}

class
|> Map.values
|> Enum.flat_map(&List.wrap/1)

# => [%{age: 20, name: "Amber"}, %{age: 22, name: "William"}, %{age: 40, name: "John"}]

實際應用時,通常會直接將上層集合傳到 flat_map 裡,並將處理的函式拿出來另外宣告,這樣可以用 pattern matching 對上層結構做更細緻的處理。

Happy hacking!

More from this blog

聊聊 Elixir 中的 type

最近有幾位朋友分別來問 Elixir 的 type 的問題,想說中文世界好像沒有比較完整的東西,就把知道的東西整理出來。 (目前) Elixir 的 type 能做什麼? tl;dr: 最主要是文件,然後在某種程度下防止錯誤。 我覺得這應該是在研究 Elixir 的 type 時最需要知道的事情了。不像 Haskell 及 F# 這種以型別著稱的 ML 系語言,Elixir / Erlang 本質上是個動態語言,所有與型別有關的標註都會被編譯器忽略。而 Erlang 內建的型別檢查工具 dia...

Oct 18, 20222 min read

Steam 上的程式教學類遊戲

農曆年期間比較有空,玩了一些之前買的遊戲。這次特別試了幾個標榜讓不會寫程式的人學寫程式的遊戲。分享一下試玩的心得。 1. 7 Billion Humans 考慮到劇情的話我最喜歡的是 7 Billion Humans。它用拖拉語法的方式下指令,一開始還蠻好上手的,但是因為只有 goto 那樣的結構,而操作的時候又是一次對所有的 worker 下指令,所以常常要想一下執行後每個人運作的順序。但是介面有正體中文,以「想要體驗一下寫程式大概是怎麼一回事」來說還蠻適合的。 2. while Tru...

Feb 24, 20201 min read

Let's (re)start from here.

最近的時間大半都花在這上面了。 算算應該是第五次弄部落格系統。算一下扣除上古時期用現成的之外,每個系統平均各寫六篇文章,也都撐不過兩年。前幾個分別用了 Refinery CMS -> jekyll -> middleman -> jekyll。想來架系統的總時數應該超過寫文章的時間 XD 而這次用上了 Gatsby + tailwindcss,除了恢復一下 GraphQL 的手感之外,這次還挑戰了不套別人做的版型,自己把類似上一個部落格的 style 刻出來。想說來分享一下這些技術的感想: G...

Jan 11, 20201 min read

Mostly Functional

31 posts

/.(ex|jsx?|rb|hs|rs|py)/, A father, a bookworm, a pluviophile. Co-organizer of http://Elixir.tw. Learning Satir, coaching & mediation.