内容简介:在 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() 有什麼威力呢 ? 讓我們繼續看下去。
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 並不相同,各為 teacher 、 assistant 與 students 。且自個 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 另外處理, 可讀性很差。
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 都不用判斷,非常優雅
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() 表示組合
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 型別。
其實 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() 只有一層
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
以上所述就是小编给大家介绍的《如何使用 Ramda 實作 wrap() ?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
营销三大算法
刘学林、刘逸春、张新春、王颖、余彬晶、刘锦炽、董少灵、沈逸超、王锐睿、孙静若 / 上海交通大学出版社 / 2018-1-31 / 88.00元
未来的营销应该是数字化的,即数字营销。以数据为本,用演算做根,数字营销能够演算生活的方方面面。在数字营销领域,市场的整个投入、产出带来什么东西?企业一定要狠清楚地知道,这是做数字营销的本质。数字营销和企业做生意的本质是一样的,目的都是以投入换取产出。 本书由正和岛数字营销部落编写,基于大量企业的案例与数据,提出了营销三大核心算法与一套全局营销系统,帮助企业CEO与营销人员科学化建立全局营销系......一起来看看 《营销三大算法》 这本书的介绍吧!