點燈坊

失くすものさえない今が強くなるチャンスよ

使用 intersection() 找出兩 Array 交集部分

Sam Xiao's Avatar 2020-03-28

若要找出兩個 Array 中共有的 Element 且不重複,Ramda 提供了 intersection()

Version

macOS Catalina 10.15.4
VS Code 1.43.2
Quokka 1.0.284
Ramda 0.27.0

Imperative

let data0 = [1, 3, 5, 3]
let data1 = [5, 7, 3, 7]

let intersection = fst => snd => {
  let result = []

  for(let x of fst) {
    for(let y of snd) {
      if (x === y) {
        if (!result.includes(x))
          result.push(x)
      }
    }
  }

  return result
}

intersection(data0)(data1) // ?

Imperative 會先建立 result array,使用兩層 array 對 fstsndfor loop,若 xy 相等且不在 result 內,則新增至 result,最後回傳 result array。

inter002

Functional

import { uniq, filter, flip, includes } from 'ramda'

let data0 = [1, 3, 5, 3]
let data1 = [5, 7, 3, 7]

let intersection = fst => snd => uniq(filter(flip(includes)(snd))(fst))

intersection(data0)(data1) // ?

Functional 思考方式如下:

  • fst array 為主體,兩個 array 共用部分必為 fst array 的一部分,故使用 filter()
  • filter() 的 callback 原本是 x => snd.includes(x),可改用 Ramda 的 includes() 產生,但 includes() 的 signature 為 a -> [a] -> Boolean,故需 flip(includes)[a] -> a -> Boolean,才能由 flip(includes)(snd) 產生 a -> Boolean 的 callback
  • 最後使用 uniq()filter() 結果使之不重複

inter003

Point-free

import { uniq, filter, flip, includes } from 'ramda'
import { compose, useWith, identity } from 'ramda'

let data0 = [1, 3, 5, 3]
let data1 = [5, 7, 3, 7]

let intersection = flip(compose(
  uniq, 
  useWith(filter, [flip(includes), identity])
))


intersection(data0)(data1) // ?

看到 uniq(filter(flip(includes))) 這麼多層,很明顯能使用 function composition 使之 point-free。

  • uniq()filter() 關係為簡單 compose() 沒問題
  • 內層 flip(includes) 會先吸收一個 argument,外層 filter() 會再吸收另一個 argument,這是很典型的 useWith() 應用
  • 由於 flip(includes) 接收的是 snd 不是 fst,最後再使用 flip() 顛倒

inter004

Ramda

import { intersection } from 'ramda'

let data0 = [1, 3, 5, 3]
let data1 = [5, 7, 3, 7]

intersection(data0)(data1) // ?

Ramda 已經提供 intersection() 可直接使用。

intersection()
[*] → [*] → [*]
找出兩個 array 中共有且不重複 element

[*]:data 為第一個 array

[*]:data 為第二個 array

[*]:回傳兩 array 共有且不重複 element 的新 array

inter000

Object

import { intersection } from 'ramda'

let data0 = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 },
]

let data1 = [
  { title: 'FP in JavaScript', price: 400 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 500 },
]

intersection(data0)(data1) // ?

intersection() 除了能用在 primitive 外,也能用在 object。

inter001

Conclusion

  • 若不知道 intersection(),也可由 includes()filter()uniq() 組合出來,唯中間必須靠 flip()useWith() 等作微調

Reference

Ramda, intersection()
Ramda, uniq()
Ramda, filter()
Ramda, flip()
Ramda, includes()
Ramda, useWith()
Ramda, compose()
Ramda, identity()