如何使用 Ramda 實作 wrap() ?

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

内容简介:在 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() ?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

jQuery基础教程

jQuery基础教程

Jonathan Chaffer、Karl Swedberg / 李松峰、卢玉平 / 人民邮电出版社 / 2009-11 / 49.00元

《jQuery基础教程(第2版)》作为《jQuery基础教程》的升级版,涵盖了jQuery 1.3的全部新特性,特别是新增了介绍jQuery UI(jQuery官方用户界面插件库)的内容。《jQuery基础教程(第2版)》前6章以通俗易懂的方式介绍了jQuery的基本概念,主要包括jQuery的选择符、事件、效果、DOM操作、AJAX支持等。随后3章从理论到实践,通过表格操作、构建功能型表单、实现......一起来看看 《jQuery基础教程》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具