[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 ...
随机推荐
- java console 到文件
System.setOut(new PrintStream(new FileOutputStream("c:\\temp\\test1.txt"))); System.out.pr ...
- 阿里云下Linux服务器安装Redis
什么是Redis? Redis是一个可以持久化的缓存框架,支持分布式缓存,简单易用.类似的框架还有memcached,是一个Key-Value形式存储的缓存框架. 可以作为缓存的框架有: EHCa ...
- 验证Xcode真伪的方法,来自苹果官网
验证Xcode真伪的方法,来自苹果官网 Xcode的验证你的版本 2015年9月22日 注意:中文为有道翻译,看下验证方法即可. 我们最近将应用程序从应用程序商店,还建有Xcode的假冒 ...
- 【BZOJ 2179】 2179: FFT快速傅立叶 (FFT)
2179: FFT快速傅立叶 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 3308 Solved: 1720 Description 给出两个n位 ...
- 【BZOJ 2711】 2711: [Violet 2]After 17 (0-1 背包)
2711: [Violet 2]After 17 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 224 Solved: 153 Description ...
- 「ZJOI2009」多米诺骨牌
「ZJOI2009」多米诺骨牌 题目描述 有一个n × m 的矩形表格,其中有一些位置有障碍.现在要在这个表格内 放一些1 × 2 或者2 × 1 的多米诺骨牌,使得任何两个多米诺骨牌没有重叠部分,任 ...
- ssm整合总结(一)--第一步之使用maven搭建一个web项目
本文内容来自:山硅谷,本文内容整合了任务2,任务3,任务4内容.http://www.gulixueyuan.com/my/course/50 1说明 1.1该项目使用的知识点有 1.1.1校验方式是 ...
- Codeforces Round #300 B. Quasi Binary 水题
B. Quasi Binary Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/538/probl ...
- poj 3468 A Simple Problem with Integers 线段树区间加,区间查询和
A Simple Problem with Integers Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://poj.org/problem?i ...
- HDU 4686 Arc of Dream (2013多校9 1001 题,矩阵)
Arc of Dream Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Tota ...