Skip to content

Commit e3564d6

Browse files
committed
Merge pull request #246 from goatslacker/functional-tools
Functional tools
2 parents e50047c + 8d5aa29 commit e3564d6

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

src/utils/fp.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const { push } = Array.prototype
2+
3+
// Disabling no-shadow so we can sanely curry
4+
/*eslint-disable no-shadow*/
5+
export function map(fn, stores) {
6+
return stores
7+
? stores.map(store => fn(store.getState()))
8+
: stores => map(fn, stores)
9+
}
10+
11+
export function filter(fn, stores) {
12+
return stores
13+
? stores.filter(store => fn(store.getState()))
14+
: stores => filter(fn, stores)
15+
}
16+
17+
export function reduce(fn, stores, acc = {}) {
18+
return stores
19+
? stores.reduce((acc, store) => fn(acc, store.getState()), acc)
20+
: stores => reduce(fn, stores)
21+
}
22+
23+
export function flatMap(fn, stores) {
24+
if (!stores) return (stores) => flatMap(fn, stores)
25+
26+
return stores.reduce((result, store) => {
27+
let value = fn(store.getState())
28+
if (Array.isArray(value)) {
29+
push.apply(result, value)
30+
} else {
31+
result.push(value)
32+
}
33+
return result
34+
}, [])
35+
}
36+
37+
export function zipWith(fn, a, b) {
38+
if (!a && !b) {
39+
return (a, b) => zipWith(fn, a, b)
40+
}
41+
42+
const length = Math.min(a.length, b.length)
43+
const result = Array(length)
44+
for (let i = 0; i < length; i += 1) {
45+
result[i] = fn(a[i].getState(), b[i].getState())
46+
}
47+
return result
48+
}

test/functional-tools-test.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { assert } from 'chai'
2+
import Alt from '../'
3+
import { createStore } from '../utils/decorators'
4+
import { map, filter, reduce, flatMap, zipWith } from '../utils/fp'
5+
6+
const alt = new Alt()
7+
8+
@createStore(alt)
9+
class Store1 {
10+
constructor() {
11+
this.a = 1
12+
this.num = 1
13+
this.arr = [1, 2, 3]
14+
}
15+
}
16+
17+
@createStore(alt)
18+
class Store2 {
19+
constructor() {
20+
this.b = 2
21+
this.num = 2
22+
this.arr = [4, 5, 6]
23+
}
24+
}
25+
26+
@createStore(alt)
27+
class Store3 {
28+
constructor() {
29+
this.c = 3
30+
this.num = 3
31+
this.arr = [7, 8, 9]
32+
}
33+
}
34+
35+
const collection = [Store1, Store2, Store3]
36+
37+
export default {
38+
'functional tools': {
39+
'currying'() {
40+
const filterTwo = filter((state) => state.num !== 2)
41+
assert.isFunction(filterTwo)
42+
assert.isArray(filterTwo(collection))
43+
44+
const mapSquare = map((state) => state.num * state.num)
45+
assert.isFunction(mapSquare)
46+
assert.isArray(mapSquare(collection))
47+
48+
const replaceState = reduce((state, nextState) => nextState)
49+
assert.isFunction(replaceState)
50+
assert.isObject(replaceState(collection))
51+
52+
const flatten = flatMap(state => state.arr)
53+
assert.isFunction(flatten)
54+
assert.isArray(flatten(collection))
55+
56+
const zip = zipWith(state => state)
57+
assert.isFunction(zip)
58+
assert.isArray(zip(collection, collection))
59+
},
60+
61+
'map'() {
62+
const value = map((state) => {
63+
return state.num * 2
64+
}, collection)
65+
66+
assert.deepEqual(value, [2, 4, 6])
67+
},
68+
69+
'filter'() {
70+
const value = filter((state) => {
71+
return state.num === 2
72+
}, collection)
73+
74+
assert(value[0] === Store2)
75+
},
76+
77+
'reduce'() {
78+
const value = reduce((n, nextState) => {
79+
return n + nextState.num
80+
}, collection, 0)
81+
82+
assert(value === 6)
83+
},
84+
85+
'zipWith'() {
86+
const value = zipWith((a, b) => {
87+
return a.arr.concat(b.arr).reduce((n, x) => n + x, 0)
88+
}, [Store1], [Store2])
89+
90+
assert(value[0] === 21)
91+
},
92+
93+
'flatMap'() {
94+
const value = flatMap((state) => {
95+
return state.num === 2
96+
? state.num
97+
: state.arr
98+
}, collection)
99+
assert.deepEqual(value, [1, 2, 3, 2, 7, 8, 9])
100+
},
101+
}
102+
}

0 commit comments

Comments
 (0)