Javascript - Using compose with reduce - javascript

I am learning functional programming with javascript. I have learned that 2 parameters are needed for reduce. Accumalator and the actual value and if we don't supply the initial value, the first argument is used. but I can't understand how the purchaseItem functions is working in the code below. can anyone please explain.
const user = {
name: 'Lachi',
active: true,
cart: [],
purchases: []
}
let history = []
const compose = (f, g) => (...args) => f(g(...args))
console.log(purchaseItem(
emptyCart,
buyItem,
applyTaxToItems,
addItemToCart
)(user, {name: 'laptop', price: 200}))
function purchaseItem(...fns) {
console.log(fns)
return fns.reduce(compose)
}
function addItemToCart (user, item) {
history.push(user)
const updatedCart = user.cart.concat(item)
return Object.assign({}, user, { cart: updatedCart })
}
function applyTaxToItems(user) {
history.push(user)
const {cart} = user
const taxRate = 1.3
const updatedCart = cart.map(item => {
return {
name: item.name,
price: item.price * taxRate
}
})
return Object.assign({}, user, { cart: updatedCart })
}
function buyItem(user) {
history.push(user)
return Object.assign({}, user, { purchases: user.cart })
}
function emptyCart(user) {
history.push(user)
return Object.assign({}, user, {cart: []})
}

Maybe it helps if you take a minimal working example and visualize the output structure:
const comp = (f, g) => x => f(g(x));
const inc = x => `inc(${x})`;
const sqr = x => `sqr(${x})`;
const id = x => `id(${x})`;
const main = [sqr, inc, inc, inc].reduce(comp, id);
console.log(main(0)); // id(sqr(inc(inc(inc(0)))))
Please note that we need id to allow redicung an empty array.

It's a way of creating a pipeline of functions whereby the output from one function is used as the parameter of the next, so we end up with a composed function that is effectively
(...args) =>
emptyCart(
buyItem(
applyTaxToItems(
addItemToCart(...args)
)
)
)
Writing the reduce out in longhand might help in understanding:
fns.reduce((acc, currentFn) => compose(acc, currentFn))

Related

after updated array list not index instantly -react.js

i need a filter with time "old to new" and "new to old"
here is my code template:
const timeNewToOld = () => {
const [paginationUsers,setPaginationUsers] = useState([])
const newToOld = users.sort((a, b) => {
return b.Time.localeCompare(a.Time)
})
setPaginationUsers(newToOld)
}
const timeOldToNew = () => {
const oldToNew = users.sort((a, b) => {
return a.Time.localeCompare(b.Time)
})
setPaginationUsers(oldToNew)
}
this functions working but, not responding instantly on web browser.
i hope i can explain with these images:
i click on the "newtoold" function and nothing changes:
i move to the next page and i'm back to the 1st page:
everything is fine. only the first time I click on the function, it doesn't get instant updates, when I change the page, the index returns to normal.
paginationUsers created here:
useEffect(() => {
const getAllData = async () => {
onSnapshot(_dbRef, (snapshot) => {
const data = snapshot.docs.map((doc) => {
return {
id: doc.id,
...doc.data(),
}
})
setUsers(data)
setUserPageCount(Math.ceil(data.length / 20))
})
}
getAllData()
}, [])
useEffect(() => {
displayUsers(users, setPaginationUsers, userCurrentPage)
}, [users, setPaginationUsers, userCurrentPage])
i hope i could explain,
happy coding..
Array.prototype.sort doesn't create a new array, so react can't know that it changed. Creating a new array should help.
const timeOldToNew = () => {
const oldToNew = [...users].sort((a, b) => {
return a.Time.localeCompare(b.Time)
})
setPaginationUsers(oldToNew)
}

Svelte derived stores and array sort

I set up a store containing a list of rides loaded from my API:
const loadRides = () => client.service('rides').find({
query: {
$sort: {
date: -1,
}
}
});
const createRides = () => {
const { subscribe, update } = writable([], async (set) => {
try {
const rides = await loadRides().then((result) => result.data);
set(rides);
} catch (e) {
console.error(e);
}
// Socket update binding?
});
subscribe((rides) => console.debug('rides', rides));
return {
subscribe,
refresh: () => loadRides().then((result) => update(() => result.data)),
};
};
export const rides = createRides();
Then I set a two derived stores for past and future rides:
export const pastRides = derived(
rides,
($rides) => $rides
.filter((ride) => ride.steps.every((step) => step.doneAt))
,
);
export const comingRides = derived(
rides,
($rides) => $rides
.filter((ride) => ride.steps.some((step) => !step.doneAt))
.sort((rideA, rideB) => {
const compare = new Date(rideA.date) - new Date(rideB.date);
console.log(rideA.date, rideB.date, compare);
return compare;
})
,
);
The sort method on the second one does not have any effect.
So I tried to put this method before the filter one. It works, but it also sort $pastRides. In fact, it is sorting the full $rides array and it make sens.
But I does not understand why the sort after filter does not work.
What did I miss?
Array.sort is mutable. Meaning, when you call rides.sort, it will sort and modify rides and return the sorted rides.
When you use derived(rides, ($rides) => ... ), the $rides you received is the original rides array that you call set(rides). So you can imagine that both the pastRides and comingRides received the same $rides array.
you can observe this behavior in this repl
To not having both derived interfere with each other, you can create a new array and sort the new array:
const sorted_1 = derived(array, $a => [...$a].sort());
you can try this out in this repl.

Ramda: How can I make this imperative reducer more declarative?

I have the following reducer function:
The first argument to the reducers is the aggregated value, and the second argument is the next value. The below reducer function is reducing over the same reaction argument but aggregating the state$ value. Each reducer function yields a new aggregated value.
/**
* Applies all the reducers to create a state object.
*/
function reactionReducer(reaction: ReactionObject): ReactionObject {
let state$ = reactionDescriptionReducer({}, reaction);
state$ = reactionDisabledReducer(state$, reaction);
state$ = reactionIconReducer(state$, reaction);
state$ = reactionOrderReducer(state$, reaction);
state$ = reactionStyleReducer(state$, reaction);
state$ = reactionTitleReducer(state$, reaction);
state$ = reactionTooltipReducer(state$, reaction);
state$ = reactionVisibleReducer(state$, reaction);
return state$;
}
const state = reactionReducer(value);
The above works but the function is fixed with the list of reducers. It seems like I should be able to do something like this with RamdaJS.
const state = R.????({}, value, [reactionDescriptionReducer
reactionDisabledReducer,
reactionIconReducer,
reactionOrderReducer,
reactionStyleReducer,
reactionTitleReducer,
reactionTooltipReducer,
reactionVisibleReducer]);
I am new to RamdaJS so forgive me if this is a noob question.
How can I execute a chain of reducers using just RamdaJS?
and constructs a new reducer, (r, x) => ..., by combining the two (2) input reducers, f and g -
const and = (f, g) =>
(r, x) => g (f (r, x), x)
all, by use of and, constructs a new reducer by combining an arbitrary number of reducers -
const identity = x =>
x
const all = (f = identity, ...more) =>
more .reduce (and, f)
Define myReducer using all -
const myReducer =
all
( reactionDisabledReducer
, reactionIconReducer
, reactionOrderReducer
// ...
)
Given a mocked implementation for these three (3) reducers -
const reactionDisabledReducer = (s, x) =>
x < 0
? { ...s, disabled: true }
: s
const reactionIconReducer = (s, x) =>
({ ...s, icon: `${x}.png` })
const reactionOrderReducer = (s, x) =>
x > 10
? { ...s, error: "over 10" }
: s
Run myReducer to see the outputs
const initState =
{ foo: "bar" }
myReducer (initState, 10)
// { foo: 'bar', icon: '10.png' }
myReducer (initState, -1)
// { foo: 'bar', disabled: true, icon: '-1.png' }
myReducer (initState, 100)
// { foo: 'bar', icon: '100.png', error: 'over 10' }
Expand the snippet below to verify the results in your browser -
const identity = x =>
x
const and = (f, g) =>
(r, x) => g (f (r, x), x)
const all = (f, ...more) =>
more .reduce (and, f)
const reactionDisabledReducer = (s, x) =>
x < 0
? { ...s, disabled: true }
: s
const reactionIconReducer = (s, x) =>
({ ...s, icon: `${x}.png` })
const reactionOrderReducer = (s, x) =>
x > 10
? { ...s, error: "over 10" }
: s
const myReducer =
all
( reactionDisabledReducer
, reactionIconReducer
, reactionOrderReducer
// ...
)
const initState =
{ foo: "bar" }
console .log (myReducer (initState, 10))
// { foo: 'bar', icon: '10.png' }
console .log (myReducer (initState, -1))
// { foo: 'bar', disabled: true, icon: '-1.png' }
console .log (myReducer (initState, 100))
// { foo: 'bar', icon: '100.png', error: 'over 10' }
You can choose whatever names you like for and and all. I could see them as part of a reducer module, like reducer.and and reducer.all
One option of utilising Ramda here would be to make use of the fact that it supports passing functions as a monad instance to R.chain (otherwise known as the Reader monad).
This lets you sequence a number of functions together that share some common environment - in your case, reaction.
We can make use of R.pipeWith(R.chain) to allow composing a series of these functions that take some input (e.g. your $state threading through each function) and returns a function that takes the environment, producing a result to pass on to the next function in the pipeline.
// Some mock functions to demonstrate
const reactionDescriptionReducer = ({...state}, reaction) =>
({ description: reaction, ...state })
const reactionDisabledReducer = ({...state}, reaction) =>
({ disabled: reaction, ...state })
const reactionIconReducer = ({...state}, reaction) =>
({ icon: reaction, ...state })
// effectively `R.pipeK`
const kleisli = R.pipeWith(R.chain)
// we need the functions going into chain to be curried
const curried = f => a => b => f(a, b)
// finally, compose the series of functions together
const reactReducer = kleisli([
curried(reactionDescriptionReducer),
curried(reactionDisabledReducer),
curried(reactionIconReducer)
])({})
// and if all goes well...
console.log(
reactReducer("someCommonReactionValue")
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
My first attempt would not involve Ramda at all, just a simple:
const makeReducer = (...fns) => (x) => fns .reduce ( (s, fn) => fn (s, x), {} )
const fn = makeReducer (
(state$, reaction) => ({...state$, foo: `<<-${reaction.foo}->>`}),
(state$, reaction) => ({...state$, bar: `=*=${reaction.bar}=*=`}),
(state$, reaction) => ({...state$, baz: `-=-${reaction.baz}-=-`})
)
console .log (
fn ( {foo: 'a', bar: 'b', baz: 'c'} )
) //~> {foo: '<<-a->>', bar: '=*=b=*=', baz: '-=-c-=-'}
While you could choose to use Ramda's reduce and flip, it doesn't seem as though they'll add much here.

Map array items through several functions

Is there a more elegant way then this to execute several functions in succession for each item in the array:
type Transform<T> = (o: T) => T;
type Item = { /* properties */ };
transform(input, transformers: Transform<Item>[]) {
const items: Item[] = getItems(input);
return items.map(item => {
let transformed = item;
tramsformers.forEach(t => transformed = t(transformed));
return transformed;
})
}
This is a great use case for reduce:
transform(input, transformers: Transform<Item>[]) {
const items: Item[] = getItems(input);
return items.map(item => transformers.reduce((val, transformer) => transformer(val), item));
}
Or perhaps more readably:
transform(input, transformers: Transform<Item>[]) {
const items: Item[] = getItems(input);
return items.map(
item => transformers.reduce(
(val, transformer) => transformer(val),
item
)
);
}
Live Example:
function getItems(input) {
return [
"abcdefg",
"1234567"
];
}
function transform(input, transformers) {
const items = getItems(input);
return items.map(item => transformers.reduce((val, transformer) => transformer(val), item));
}
const result = transform("x", [
v => v.toUpperCase(),
v => v.substring(1, v.length - 1)
]);
console.log(result);
As Nitzan Tomer points out, we could do away with the items constant:
transform(input, transformers: Transform<Item>[]) {
return getItems(input).map(
item => transformers.reduce(
(val, transformer) => transformer(val),
item
)
);
}
I frequently keep those sorts of things for debugging, but some good debuggers now make it easy to see the return value of functions before they return (Chrome's does), so if you removed it, you could step into getItems to see the items before the map.
Here's a bit more reusable version, based on #T.J.Crowder's answer:
export type Transform<T> = (o: T) => T;
export function pipe<T>(sequence: Transform<T>[] = []) {
return (item: T) => sequence.reduce((value, next) => next(value), item);
}
transform(input, transformers?) {
return getItems(input).map( pipe(transformers) );
}
Note that type is inferred from getItems(input) and return type is transform(): Item[].

Create an Observable object from a list of Observables

I'm still wrapping my head around RxJS and there is this pattern I keep running into and that I would like to find a more elegant way to write.
Implementing the model part of a Model-View-Intent pattern component, I have a function that takes actions as input an returns a single state$ Observable as output.
function model(actions) {
const firstProperty$ =
const anotherProperty$ = …
// Better way to write this?
const state$ = Rx.Observable.combineLatest(
firstProperty$, anotherProperty$,
(firstProperty, anotherProperty) => ({
firstProperty, anotherProperty
})
);
return state$;
}
So my model method computes a bunch of observables, every one of them emit items that represents a part of the state of my application. That is fine.
But how to I cleanly combine them into a single one observable that emits states, each state being a single object whose keys are the initial observable names?
I borrowed this pattern from https://github.com/cyclejs/todomvc-cycle :
function model(initialState$, actions){
const mod$ = modifications(actions)
return initialState$
.concat(mod$)
.scan( (state, mod) => mod(state))
.share()
}
function modifications(actions){
const firstMod$ = actions.anAction$.map(anAction => (
state => ({ ...state,
firstProperty: anAction.something
})
const secondMod$ = actions.otherAction$.map(otherAction => (
state => ({ ...state,
firstProperty: otherAction.something,
secondProperty: aComputation(otherAction)
})
return Rx.Observable.merge([firstMod$, secondMod$ ]).share()
}
In the main function :
const initialState$ = Rx.Observable.from({})
const actions = intent(DOM)
const state$ = model(initialState$, actions).share()
Using help from CHadrien, here is a working solution.
const prop1$ = Rx.Observable.of('foo');
const prop2$ = Rx.Observable.of('bar');
const prop3$ = Rx.Observable.of('baz');
const prop4$ = Rx.Observable.of('foobar');
function combineObservables(objectOfObservables) {
const keys = Object.keys(objectOfObservables);
const observables = keys.map(key => objectOfObservables[key]);
const combined$ = Rx.Observable.combineLatest(
observables, (...values) => {
var obj = {};
for (let i = 0 ; i < keys.length ; i++) {
obj[keys[i]] = values[i];
}
return obj;
}
);
return combined$;
}
combineObservables({prop1$, prop2$, prop3$, prop4$}).subscribe(x => console.log(x));
And the result:
[object Object] {
prop1$: "foo",
prop2$: "bar",
prop3$: "baz",
prop4$: "foobar"
}

Categories