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的更多相关文章

  1. [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 ...

  2. Scalaz(48)- scalaz-stream: 深入了解-Transducer: Process1-tee-wye

    在上一篇讨论里我们介绍了Source,它的类型款式是这样的:Process[F[_],O].Source是通过await函数来产生数据流.await函数款式如下: def await[F[_], A, ...

  3. java 深入技术二(Collection)

    1. java集合 存储和管理多个java对象 包括很多java类和接口 Collection List                              Set ArrayList  Lin ...

  4. 设计一个泛型类Collection

    要求:设计一个泛型类Collection,它存储object对象的集合(在数组中),以及该集合当前的大小.提供public方法isEmtpy,makeEmpty,insert,remove,isPre ...

  5. java中集合Collection转list对象

    参考:java中集合Collection转list对象 首先我的需求是获取到购物车列表,购物车列表是一个Map对象,构造方法获取购物项,这里购物项是Collection对象 // 购物项集合,K商品I ...

  6. java collection.frequency方法

    collection.frequency方法,可以统计出某个对象在collection中出现的次数 比如: frequency(Collection<?> c, Object o)     ...

  7. PASCAL VOC数据集The PASCAL Object Recognition Database Collection

    The PASCAL Object Recognition Database Collection News 04-Apr-07: The VOC2007 challenge development ...

  8. Java集合----Collection工具类

    Collections 工具类 Collections 是一个操作 Set.List 和 Map 等集合的工具类 Collections 中提供了大量方法对集合元素进行排序.查询和修改等操作,还提供了 ...

  9. Java基础学习-Collection体系结构和迭代测试

    package Collection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterat ...

随机推荐

  1. bound和unbound方法,类的绑定和非绑定是什么

    作者:灵剑链接:https://www.zhihu.com/question/41006598/answer/148994582来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

  2. adroid swipeRefreshLayout无法显示进度条的问题

    一句话经验:必须嵌套scrollerview或者listview

  3. fir.im Weekly - 嘘,关于***!

    上 Github 交友刷 StackOverflow 解惑,是攻城狮必备技能,加快打怪练级速度.关于,@左耳朵耗子 在微博上分享了一篇文档,轻一点教你建一个VPN服务器,重一点到教你在路由器上***, ...

  4. 如何使用Eclipse插件—Easy Explorer

    Easy Explorer是一个Eclipse插件,主要用于快速浏览项目文件的目录,实用性比较强. 使用方法如下: 1.下载Easy Explorer,从此处下载EasyExplorer:http:/ ...

  5. MySQL5.7添加授权账号及修改默认端口

    1.修改默认端口 打开配置文件 vim /etc/my.cnf 分别添加端口在client.mysql节点 [client] port=15099 [mysqld] port=15099 需要注意se ...

  6. Linux知识(2)----中文输入法安装

    Ubantu14.04在English的环境下,没有中文输入法,自带的ibus不完整.现在基于ibus框架,有几个比较好用的输入法,如sunpingyin和google pinying,还有五笔的输入 ...

  7. Eclipse打开.class文件

    1.准备材料 jad.exe    下载地址  https://varaneckas.com/jad/,根据系统去选择(就当是废话)  下载下来的是jad158g.win.zip压缩文件,解压后有个j ...

  8. Delphi 的插件框架 WisdomPluginFramework

    WisdomPluginFramework是融合OSGI微内核理念 + Eclipse的扩展点概念而精心设计的轻量级插件框架, 由Delphi实现,但可以使用于Delphi.BCB.VC++中,提供非 ...

  9. C语言之数组中你所不在意的重要知识

    #include<stdio.h> void simpleArray(); void main() { simpleArray(); } //数组的简单操作 void simpleArra ...

  10. hashmap实现原理2

    put和get都首先会调用hashcode方法,去查找相关的key,当有冲突时,再调用equals(这也是为什么刚开始就重温hashcode和equals的原因)! HashMap基于hashing原 ...