[Transducer] Make Transducer works for Iteratable collection and Object
We've seen how we can transduce
from arrays or other iterables, but plain objects aren't iterable in Javascript. In this lesson we'll modify our transduce()
function so that it supports iterating from plain objects as well, treating each key value pair as an entry in the collection.
To do this we'll be using a lodash function called entries.
The whole point to make collection works for Object type is because when we use for.. of loop, Object is not itertable type, so Object still cannot be used. The fix that problem, we can use 'entries' from lodash, to only get value as an array from the Object, so that we can loop though the array.
import {isPlainObject, entries} from 'lodash';
import {map, into} from '../utils'; let transduce = (xf /** could be composed **/, reducer, seed, _collection) => { const transformedReducer = xf(reducer);
let accumulation = seed; const collection = isPlainObject(_collection) ? entries(_collection) : _collection; for (let value of collection) {
accumulation = transformedReducer(accumulation, value);
} return accumulation;
}; const objectValues = obj => {
return into([], map(kv => kv[1]), obj);
}; objectValues({one: 1, two: 2});
---
const { isObject, isArray } = require('crocks');
const { toPairs } = require('ramda'); const data = [,,];
const inc = x => x + ;
const double = x => * x;
const lessThanThree = x => x < ;
const toUpper = s => s.toUpperCase();
const isVowel = char => ['a', 'e', 'i', 'o', 'u'].includes(char.toLowerCase());
const compose = (...fns) => (...args) => fns.reduce((acc, fn) => [fn.call(null, ...acc)], args)[]
////////////////////
/**
* Problem: We loop over array 3 times! We want to loop over only once
* in order to improve the profermance.
*/
const res1 = data
.filter(lessThanThree)
.map(double)
.map(inc) console.log(res1) // [3,5] ////////////////////////////////
/**
* Problem: it is not pure function and we do mutation. But it is faster
* than we do .filter.map.map style, because it only loop the array once.
*/
let res2 = [];
data.forEach((x) => {
let item;
if (lessThanThree(x)) {
item = inc(double(x))
res2.push(item);
}
})
console.log(res2) // [3,5] ////////////////////////////////
/**
* Good: We avoid the mutation and can be write as pure function and it only loop once!
* Problem: But we lose our function composion style! We still want .filter.map.map styling.
* Meanwhile it should be profermance wise.
*/
const res3 = data.reduce((acc, curr) => {
if (lessThanThree(curr)) {
acc.push(inc(double(curr)));
}
return acc;
}, []);
console.log(res3); // [3,5] ////////////////////////////////////
//data.reduce(reducer, seed), reducer is something we can compose!
//Because reducer :: (acc, curr) => acc
//For every reducer functions' signature are the same.
//If the function sinature are the same, then we can compose function together!
const _mapReducer = (xf, array) =>
array.reduce((acc, curr) => {
acc.push(xf(curr))
return acc;
}, []);
const _filterReducer = (xf, array) =>
array.reduce((acc, curr) => {
if (xf(curr)) acc.push(curr);
return acc;
}, []);
// To make fns easy to compose, we extract 'array' data & init value
const mapReducer = (xf) => ((acc, curr) => {
acc.push(xf(curr))
return acc;
});
const filterReducer = pred => ((acc, curr) => {
if (pred(curr)) acc.push(curr);
return acc;
});
// now mapReducer and filterReducer both have the same function signature.
console.log(data.reduce(mapReducer(double), [])); // [2,4,6]
console.log(data.reduce(mapReducer(inc), [])); // [2,3,4]
console.log(data.reduce(filterReducer(lessThanThree), [])); // [1,2] // In order to compose reudcers together we need to make mapReducer and filterReducer as high order functions to take reducer as arguement
// Take a reducer as input and return a reducer signature as output is the key to do composion!
const map = xf => reducer => ((acc, curr) => {
acc = reducer(acc, xf(curr))
return acc;
});
const filter = pred => reducer => ((acc, curr)=> {
if (pred(curr)) acc = reducer(acc, curr)
return acc;
})
// For mapReducer and filterReducer, we both do acc.push()
// therefore we can extrat this as base reducer
const pushReducer = (acc, value) => {
acc.push(value);
return acc;
}; /**
* Now we are able to use functional style and loop the array only once!s
*/
const doulbeLessThanThree = compose(
map(inc),
map(double),
filter(lessThanThree)
)
const res5 = data.reduce(doulbeLessThanThree(pushReducer), []);
console.log(res5); // [3,5] ////////////////////////////////////
// Define our transducer!
/**
* transducer :: ((a -> b -> a), (a -> b -> a), [a], [a]) -> [a]
* @param {*} xf: base reducer
* @param {*} reducer: the composion redcuer signature
* @param {*} seed : init value
* @param {*} collection : data
*/
const _transducer = (xf, reducer, seed, collection) => {
return collection.reduce(xf(reducer), seed);
}
const res6 = _transducer(doulbeLessThanThree, pushReducer, [], data);
console.log(res6); // [3,5] const transducer = (xf, reducer, seed, collection) => {
let acc = seed; collection = isObject(collection) ? toPairs(collection): collection const transformReducer = xf(reducer);
for (let curr of collection) {
acc = transformReducer(acc, curr)
} return acc;
} const res7 = transducer(
compose(filter(isVowel), map(toUpper)),
(acc, curr) => acc + curr,
'',
'transducer'
);
console.log("", res7); // AUE const numMap = new Map()
numMap.set('a', );
numMap.set('b', );
numMap.set('c', );
numMap.set('d', );
const res8 = transducer(
doulbeLessThanThree,
pushReducer,
[],
numMap.values()
);
console.log("", res8); // [3,5] /**
* into helper
* transducer = (xf, reducer, seed, colllection)
* Until so far, we have to know what kind of base reducer we need to use
* for example, push reduer for array, concat reducer for string...
*
* The idea of into helper is to let transducer to figure out what reducer
* we want to use automaticlly instead of we telling transducer which one to use
*
* into:: (to, xf, collection)
*/ const objectReducer = (obj, value) => Object.assign(obj, value);
const into = (to, xf, collection) => {
if (Array.isArray(to)) {
return transducer(xf, pushReducer, to, collection);
} else if (isObject(to)) {
return transducer(xf, objectReducer, to, collection)
}
throw new Error('into only supports arrays and objects as `to`');
} /**
* seq helper
* Different from into help, seq helper will infer the collection type
*/
const seq = (xf, collection) => {
if (isArray(collection)) {
return transducer(xf, pushReducer, [], collection);
} else if (isObject(collection)) {
return transducer(xf, objectReducer, {}, collection)
}
throw new Error('seq : unsupport collection type');
}
console.log(seq(compose(
filter(x => x < ),
map(x => x * )
), [,,])); const filp = map(([k, v]) => ({[v]: k}));
console.log(seq(filp, {one: , two: })); /**{1: 'one, 2: 'two'} */
[Transducer] Make Transducer works for Iteratable collection and Object的更多相关文章
- [Javascript] Transduce over any Iteratable Collection
So far we've been transducing by manually calling .reduce() on arrays, but we want to be able to tra ...
- Scalaz(48)- scalaz-stream: 深入了解-Transducer: Process1-tee-wye
在上一篇讨论里我们介绍了Source,它的类型款式是这样的:Process[F[_],O].Source是通过await函数来产生数据流.await函数款式如下: def await[F[_], A, ...
- java 深入技术二(Collection)
1. java集合 存储和管理多个java对象 包括很多java类和接口 Collection List Set ArrayList Lin ...
- 设计一个泛型类Collection
要求:设计一个泛型类Collection,它存储object对象的集合(在数组中),以及该集合当前的大小.提供public方法isEmtpy,makeEmpty,insert,remove,isPre ...
- java中集合Collection转list对象
参考:java中集合Collection转list对象 首先我的需求是获取到购物车列表,购物车列表是一个Map对象,构造方法获取购物项,这里购物项是Collection对象 // 购物项集合,K商品I ...
- java collection.frequency方法
collection.frequency方法,可以统计出某个对象在collection中出现的次数 比如: frequency(Collection<?> c, Object o) ...
- PASCAL VOC数据集The PASCAL Object Recognition Database Collection
The PASCAL Object Recognition Database Collection News 04-Apr-07: The VOC2007 challenge development ...
- Java集合----Collection工具类
Collections 工具类 Collections 是一个操作 Set.List 和 Map 等集合的工具类 Collections 中提供了大量方法对集合元素进行排序.查询和修改等操作,还提供了 ...
- Java基础学习-Collection体系结构和迭代测试
package Collection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterat ...
随机推荐
- CentOS服务器安装Telnet来远程连接服务器
0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.前言 在连接远程服务器时有很多种连接方式,如SSH.telnet.SFTP等.但是如果大家在docker上面安装gitlab做 ...
- JMS介绍:我对JMS的理解和认识
[ZT]JMS介绍:我对JMS的理解和认识 转自:http://blog.csdn.net/KimmKing/archive/2011/06/30/6577021.aspx,感谢作者KimmKing ...
- android 网络
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha android - async - http 安卓 异步 超文本传输协议 xUtil a ...
- SPOJ694 DISUBSTR --- 后缀数组 / 后缀自动机
SPOJ694 DISUBSTR 题目描述: Given a string, we need to find the total number of its distinct substrings. ...
- Android Studio 2.3更换默认的ConstraintLayout布局
首先打开你的Android Sudio安装目录,我的为D:\Program Files\Android\Android Studio,进入到以下文件夹\plugins\android\lib\temp ...
- SPOJ 10628. SPOJ COT Count on a tree 可持久化线段树
这题是裸的主席树,每个节点建一棵主席树,再加个lca就可以了. 历尽艰辛,终于A掉了这一题,这般艰辛也显示出了打代码的不熟练. 错误:1.lca倍增的时候i和j写反了,RE了5次,实在要吸取教训 2. ...
- 02-MariaDB主从安装SpringBoot整合MyBatis配置
关于MariaDB的介绍 MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为My ...
- asp.net 将repeater上数据导出到excel
1,首先得到一个DataTable public DataTable GetTable(string sql) { SqlConnnection con=new SqlConnection(Confi ...
- Codeforces Round #300 D. Weird Chess 水题
D. Weird Chess Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/538/proble ...
- PAT甲级1107. Social Clusters
PAT甲级1107. Social Clusters 题意: 当在社交网络上注册时,您总是被要求指定您的爱好,以便找到一些具有相同兴趣的潜在朋友.一个"社会群体"是一群拥有一些共同 ...