如何使用 Ramda 實作 wrap() ?

栏目: 编程语言 · 发布时间: 6年前

内容简介:在 Elixir 的部落格發現一個很帥的VS Code 1.32.3Quokka 1.0.200

在 Elixir 的部落格發現一個很帥的 wrap() Operator,除了可以將 Array 攤平,還可以讓 null 優雅的消失,特別將其 Porting 到 Ramda。

Version

VS Code 1.32.3

Quokka 1.0.200

Ramda 0.26.1

wrap()

import { pipe, filter, identity, unnest, of } from 'ramda';

// wrap :: a -> [a]
const wrap = pipe(
  of,
  unnest,
  filter(identity)
);

console.log(wrap(1));
console.log(wrap(null));
console.log(wrap([2, 3]));

wrap() 的功能如下:

null

of() 先將 value 變成 array,這使得 wrap(1) 成為 [1]

wrap([2, 3]) 則會變成 [[2, 3]] ,顯然多了一層 array,所以還要配合 unnest() 攤平一層,但這對 wrap(1) 沒有影響,因為只有一層的 array 對 unnest() 無效。

filter(identity) 則是針對 wrap(null) ,因為 null !== null ,因此會被 filter() 濾掉,也就是 null 不會新增進 array,但為其他 primitive value 與 array 則毫無影響。

wrap() 有什麼威力呢 ? 讓我們繼續看下去。

如何使用 Ramda 實作 wrap() ?

Imperative

const data = [
  { teacher: { name: 'John', age: 40 } },
  { assistant: null },
  { students: [
    { name: 'Amber', age: 20 },
    { name: 'William', age: 22 }
  ] }
];

// getResult :: [a] -> [b]
const getResult = data => {
  const result = [];

  for (let obj of data) {
    for (let obj2 of Object.values(obj)) {
      if (obj2 !== null && !Array.isArray(obj2)) {
        result.push(obj2);
      }

      if (Array.isArray(obj2)) {
        for (let obj3 of Object.values(obj2)) {
          if (obj3 !== null) {
            result.push(obj3);
          }
        }
      }
    }
  }

  return result;
};

console.dir(getResult(data));

data 為 array,每個 object 的 key 並不相同,各為 teacherassistantstudents 。且自個 value 亦為 object、null 與 array,除了 key 不同,value 格式也不同。

[ { name: 'John', age: 40 }, 
  { name: 'Amber', age: 20 }, 
  { name: 'William', age: 22 } ]

若我們希望結果為一層 array,且僅包含 object,並不包含 null

for (let obj of data) {
}

若使用 Imperative,由於 data 為 array,所以要先使用 for loop 展開。

for (let obj2 of Object.values(obj)) {
  if (obj2 !== null && !Array.isArray(obj2)) {
    result.push(obj2);
  }
}

由於需求不顯示 null ,因此要使用 if 先過濾 null

array 也必須排除,因為 array 稍後要特別處理。

if (Array.isArray(obj2)) {
  for (let obj3 of Object.values(obj2)) {
    if (obj3 !== null) {
      result.push(obj3);
    }
  }
}

由於 students 為 array,因此又要使用 for loop 加以展開。

可以發現 Imperative 寫法,需動用三層 for loop ,且還必須用 if 判斷 null 與 array 另外處理, 可讀性很差。

如何使用 Ramda 實作 wrap() ?

chain()

import { pipe, filter, identity, unnest, of, values, chain } from 'ramda';

const data = [
  { teacher: { name: 'John', age: 40 } },
  { assistant: null },
  { students: [
    { name: 'Amber', age: 20 },
    { name: 'William', age: 22 }
  ] }
];

// wrap :: a -> [a]
const wrap = pipe(
  of,
  unnest,
  filter(identity)
);

// getResult :: [a] -> [b]
const getResult = pipe(
  chain(x => wrap(values(x))),
  unnest
);
console.dir(getResult(data));

若使用了 wrap() ,則 null 不會進 array,會優雅的消失,因此不用特別判斷。

chain() 雖然號稱是 flatMap() ,但實際上是 pipe(map, unnest) ,只能使 array 攤平一層,這對 teacher 是有用的,但 students 還有一層,因此必須再使用 pipe(chian, unnest) 繼續攤平。

可以發現使用了 wrap() 之後,除了看不到 for loop 外,連 null 與 array 都不用判斷,非常優雅

如何使用 Ramda 實作 wrap() ?

Point-free

import { pipe, filter, identity, unnest, of, values, chain, compose } from 'ramda';

const data = [
  { teacher: { name: 'John', age: 40 } },
  { assistant: null },
  { students: [
    { name: 'Amber', age: 20 },
    { name: 'William', age: 22 }
  ] }
];

// wrap :: a -> [a]
const wrap = pipe(
  of,
  unnest,
  filter(identity)
);

// getResult :: [a] -> [b]
const getResult = pipe(
  chain(compose(wrap, values)),
  unnest
);
console.dir(getResult(data));

其實之前的寫法,可讀性已經很高,若想將 chian() 的 callback 進一步 Point-free,可使用 compose()wrap()values() 加以組合。

實務上對架構會使用 pipe() 顯示流程,callback 會使用 compose() 表示組合

如何使用 Ramda 實作 wrap() ?

Composition

import { pipe, filter, identity, unnest, of, values, chain } from 'ramda';

const data = [
  { teacher: { name: 'John', age: 40 } },
  { assistant: null },
  { students: [
    { name: 'Amber', age: 20 },
    { name: 'William', age: 22 }
  ] }
];

// wrap :: a -> [a]
const wrap = pipe(
  of,
  unnest,
  filter(identity)
);

// getResult :: [a] -> [b]
const getResult = pipe(
  chain(values),
  chain(wrap),
  unnest
);
console.dir(getResult(data));

chain() 支援的 data 為 Chain 型別,而 Chain 型別一定也是 Functor 型別。

如何使用 Ramda 實作 wrap() ?

其實 array 既是 Functor 也是 Chain ,而 Functor 的基本特性就是支援數學的 identity 與 composition。

Composition 就是 chain(x => f(g(x))) 等於 pipe(chain(x => g(x)), chain(x => f(x)) ,因此可將 compose() 拆開,改成兩次 chain()

要不要將 compose() 拆開改成 pipe() 則見仁見智,最少這是 Functor 的基本特性:composition,可以等效替換,可讓整個流程只使用 pipe() ,讓 chain() 只有一層

如何使用 Ramda 實作 wrap() ?

Conclusion

  • Elixir 的 wrap() 理念很好,除了讓 array 自動減少一層外,還可讓 null 優雅的消失,他山之石可以攻錯,我們也可以在 Ramda 實現 wrap()
  • chain() 雖然號稱是 flatMap() ,其實是 compose(unnest, map) ,只能攤平一層 array
  • chain() 雖然支援的是 Chain ,而 Chain 就是 Functor ,且 array 既是 Functor 也是 Chain ,因此也支援 Functor 的基本特性:Composition,因此可將 compose 解開改用 pipe

Reference

Taiansu , List 操作小技巧

Ramda , of()

Ramda , unnest()

Ramda , identity()

Ramda , filter()

Ramda , chain()

Ramda , pipe()

Ramda , compose()

Ramda , values()

Fantasy Land Specification , Functor


以上所述就是小编给大家介绍的《如何使用 Ramda 實作 wrap() ?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

营销三大算法

营销三大算法

刘学林、刘逸春、张新春、王颖、余彬晶、刘锦炽、董少灵、沈逸超、王锐睿、孙静若 / 上海交通大学出版社 / 2018-1-31 / 88.00元

未来的营销应该是数字化的,即数字营销。以数据为本,用演算做根,数字营销能够演算生活的方方面面。在数字营销领域,市场的整个投入、产出带来什么东西?企业一定要狠清楚地知道,这是做数字营销的本质。数字营销和企业做生意的本质是一样的,目的都是以投入换取产出。 本书由正和岛数字营销部落编写,基于大量企业的案例与数据,提出了营销三大核心算法与一套全局营销系统,帮助企业CEO与营销人员科学化建立全局营销系......一起来看看 《营销三大算法》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具