點燈坊

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

使用 takeLastWhile() 根據自訂條件取得 Array 最後幾筆資料

Sam Xiao's Avatar 2019-07-17

takeLast() 只能取得 Array 固定最後筆資料,若想自行提供自訂條件 Predicate,就得使用 takeLastWhile()

Version

macOS Mojave 10.14.5
VS Code 1.36.1
Quokka 1.0.233
Ramda 0.26.1

let data = [2, 4, 1, 3];

// takeLastWhile :: (a → Boolean) → [a] → [a]
let takeLastWhile = pred => arr => {
  let result = [];

  for(let i = arr.length - 1; i >= 0; i--) {
    if (pred(arr[i]))
      result.unshift(arr[i]);
    else
      break;
  }

  return result;
};

takeLastWhile(x => x % 2)(data); // ?

takeLastWhile() 意義為當 predicate 為 true 時從最後取得 element,直到 false 時停止取得。

建立 takeLastWhile(),imperative 會先建立回傳 result array,i 從最後 index 開始,當 predicate 成立時,從 result 前面塞進 element,不成立馬上離開 for loop,最後回傳 result

takewhile006

Functional

import { pipe, reverse, takeWhile } from 'ramda';

let data = [2, 4, 1, 3];

// takeLastWhile :: (a → Boolean) → [a] → [a]
let takeLastWhile = pred => pipe(
  reverse,
  takeWhile(pred),
  reverse
);

takeLastWhile(x => x % 2)(data); // ?

若一開始不知道 takeLastWhile() 也沒關係,可藉由已知的 takeWhile() 加以組合:

  • 使用 reverse() 將 array 反轉
  • 使用 takeWhile() 根據 predicate 條件取得資料
  • 使用 reverse() 再將 array 反轉

最後使用 pipe() 組合所有 function,非常清楚。

takewhile000

Point-free

import { pipe, reverse, takeWhile, useWith, identity } from 'ramda';

let data = [2, 4, 1, 3];

// takeLastWhile :: (a → Boolean) → [a] → [a]
let takeLastWhile = useWith(
  pipe(takeWhile, reverse), [
    identity, 
    reverse
  ]
);

takeLastWhile(x => x % 2)(data); // ?

也可以使用 useWith() 進一步將 pred point-free,由 pipe() 寫法可發現:

  • reverse() 看似最後執行,但實則 pipe(takeWhile, reverse) 才是最後執行 function
  • 第一個 argument 為 predicate,因為不變,所以為 identity()
  • 第二個 argument 為 reverse()

由於第一個 argument 與第二個 argument 並不相關,明顯暗示需使用 useWith() 才能 point-free。

takewhile005

Ramda

import { takeLastWhile } from 'ramda';

let data = [2, 4, 1, 3];

takeLastWhile(x => x % 2)(data); // ?

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

takeLastWhile()
(a → Boolean) → [a] → [a]
根據自訂條件取得 array 最後幾筆資料

(a -> Boolean):自訂條件 predicate

[a]:data 為 array

[a]:回傳 array 最後幾筆資料

takewhile001

Point-free

import { takeLastWhile, compose, modulo, __, equals } from 'ramda';

let data = [2, 4, 1, 3];

let pred = compose(
  equals(1),
  modulo(__, 2)
);

takeLastWhile(pred)(data); // ?

也可將 x => x % 2 進一步 point-free。

  • 使用 modulo() 取得餘數,相當於 % 2
  • 使用 equals() 判斷相等,相當於 === 1

最後使用 compose() 組合所有 function,非常清楚。

takewhile002

Object

import { takeLastWhile  } from 'ramda';

let data = [
  { title: 'FP in JavaScript', price: 300 },
  { title: 'RxJS in Action', price: 203 },
  { title: 'Speaking JavaScript', price: 101 }
];

console.dir(takeLastWhile(x => x.price % 2)(data)); 

takeWhile() 也能用在 object。

takewhile003

Point-free

import { takeLastWhile, compose, prop, modulo, __, equals  } from 'ramda';

let data = [
  { title: 'FP in JavaScript', price: 300 },
  { title: 'RxJS in Action', price: 203 },
  { title: 'Speaking JavaScript', price: 101 }
];

let pred = compose(
  equals(1),
  modulo(__, 2), 
  prop('price')
);

console.dir(takeLastWhile(pred)(data));

也可將 x => x.price % 2 進一步 point-free。

  • 使用 prop() 取得 property 值,相當於 x.price
  • 使用 modulo() 取得餘數,相當於 % 2
  • 使用 equals() 判斷相等,相當於 === 1

最後使用 compose() 組合所有 function,非常清楚。

takewhile004

Conclusion

  • takeLastWhle()takeWhile() 用法很類似,但 takeWhile() 是取得前幾筆資料,而 takeLastWhile() 是取得最後幾筆資料
  • takeLastWhile() 除了用在 array,也能用在 string

Reference

Ramda, takeLastWhile()
Ramda, takeWhile()
Ramda, pipe()
Ramda, reverse()
Ramda, useWith()
Ramda, identity()
Ramda, compose()
Ramda, equals()
Ramda, modulo()
Ramda, prop()