Created
October 23, 2018 18:11
-
-
Save rjdestigter/5306eef85534f73f826d6988f84812d4 to your computer and use it in GitHub Desktop.
map and flatMap implementation for selectors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Fruit { | |
isRotten: boolean | |
} | |
interface Apple extends Fruit { | |
apple: true | |
} | |
interface Banana extends Fruit { | |
banana: true | |
} | |
interface State { | |
apples: Apple[] | |
bananas: Banana[] | |
selected: Set<'Apples' | 'Bananas'> | |
} | |
const isRotten = <F extends Fruit>(fruit: F) => fruit.isRotten | |
const edibleFruits = <F extends Fruit>(fruits: F[]) => fruits.filter(fruit => !isRotten(fruit)) | |
const apples$ = (state: State) => state.apples | |
const bananas$ = (state: State) => state.bananas | |
const selectedFruits$ = (state: State) => state.selected | |
const edibleApples$ = map(apples$)(edibleFruits) | |
const edibleBananas$ = map(bananas$)(edibleFruits) | |
const selectedFruitsArray$ = map(selectedFruits$)(selected => Array.from(selected)) | |
const fruitBasket$ = flatMap(selectedFruitsArray$)(fruits => { | |
const selectors$: Selector<State, Fruit[]>[] = fruits.map(fruit => { | |
switch (fruit) { | |
case 'Apples': | |
return apples$ | |
case 'Bananas': | |
return bananas$ | |
} | |
}) | |
return createSelector(selectors$, (...allFruits) => ([] as Fruit[]).concat(...allFruits)) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createSelector, defaultMemoize, Selector } from 'reselect' | |
/** | |
* Map (fmap) from selector A to selector B. | |
* | |
* @param f Function that maps a -> b | |
* @param selector$ Selector that, given state S returns A | |
* @returns A selector that, given state S, returns B | |
*/ | |
export const map = <S, A>(selector$: Selector<S, A>) => <B>(f: (a: A) => B) => | |
createSelector(selector$, outputA => f(outputA)) | |
/** | |
* Flat map a selector of a selector. Also known as "bind" (haskell) or "chain" (fp-ts) | |
* @param f Function that, given the result of selector A returns a selector of B | |
* @param a$ A selector of A | |
* @returns A function that, given state S, returns B | |
*/ | |
export const flatMap = <S, A>(a$: Selector<S, A>) => <B>(f: (a: A) => Selector<S, B>) => { | |
let previousA: A | undefined | |
let b$: Selector<S, B> | undefined | |
return defaultMemoize((state: S) => { | |
const nextA = a$(state) | |
if (nextA !== previousA || b$ == null) { | |
previousA = nextA | |
b$ = f(previousA) | |
} | |
return b$(state) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment