Reputation: 4254
Say I got these two filters:
const getNamesStartingWith = (letter) => persons => {
return persons.filter(person => person.name.startsWith(letter);
}
const getPersonsStartingWithZipCode = (zipCode) => persons => {
return persons.filter(person => person.address.zipCode.startsWith(zipCode);
}
I want to make a generic pipe filter.
const pipeFilter = (funcArr, args) => arr => {
...
}
The funcArr
is an array of the functions to run. The args
is a double array where the index is the arguments of the functions.
So with my example functions it would be:
const pipeFilter = ([getNamesStartingWith, getPersonsStartingWithZipCode], [['J'], [4]]) => persons => {..}
The argument for getNamesStartingWith
is J
. The argument for getPersonsStartingWithZipCode
is 4
If I would to it 'manually' I would to something like:
export const doPipeFiltering = (funcArr: any[], args: any[]) => (arr) => {
return funcArr.reduce((acc, func, index) => {
let filterdArr;
if (index === 0) {
filterdArr = func(...args[index])(arr);
} else {
filterdArr = func(...args[index])(acc);
}
acc = filterdArr;
return acc;
}, []);
};
Works. But I would like to do it in RamdaJs so I can use all neat functions there.
I don't find how can apply arguments for differet filter functions in Ramda. Is it possible?
Upvotes: 0
Views: 284
Reputation: 50807
You can definitely make this a fair bit cleaner using Ramda functions. Here's one approach:
const doPipeFiltering = curry ( (funcArr, args, arr) =>
reduce ( (acc, func) => func (acc), arr, zipWith (apply, funcArr, args) ))
const getNamesStartingWith = (letter) => (persons) => {
return persons.filter(person => person.name.startsWith(letter))
}
const getPersonsStartingWithZipCode = (zipCode) => persons => {
return persons.filter(person => person.address.zipCode.startsWith(zipCode))
}
const people = [
{name: 'Amanda', address: {zipCode: '86753'}},
{name: 'Aaron', address: {zipCode: '09867'}},
{name: 'Amelia', address: {zipCode: '85309'}},
{name: 'Bob', address: {zipCode: '67530'}}
]
console .log (
doPipeFiltering (
[getNamesStartingWith, getPersonsStartingWithZipCode],
[['A'], ['8']],
people
)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {curry, reduce, zipWith, apply} = R </script>
But I would suggest that this is not a very ergonomic API. First of all, if the filtering functions had an entirely generic interface, it might not be so bad, but now you have to supply two separate array parameters, whose values must be synchronized. That to me is a recipe for disaster.
Secondly, what we have to create for this is functions that filter a list. I would find it much cleaner if the code handled the filtering, and we only supplied a set of simple predicates.
So this is an alternative suggestion, for an API that I think is much cleaner:
const runFilters = useWith (filter, [allPass, identity] )
// or one of these
// const runFilter = curry((filters, xs) => filter(allPass(filters), xs))
// const runFilters = curry ((filters, xs) => reduce ((a, f) => filter (f, a), xs, filters))
const nameStartsWith = (letter) => (person) => person.name.startsWith (letter)
const zipStartsWith = (digits) => (person) => person.address.zipCode.startsWith (digits)
const myFilter = runFilters ([nameStartsWith ('A'), zipStartsWith ('8')])
const people = [
{name: 'Amanda', address: {zipCode: '86753'}},
{name: 'Aaron', address: {zipCode: '09867'}},
{name: 'Amelia', address: {zipCode: '85309'}},
{name: 'Bob', address: {zipCode: '67530'}}
]
console .log (
myFilter (people)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {useWith, filter, allPass, identity} = R </script>
Note that here the main function is simpler, the filter predicates are simpler, and the call is much more explicit. Especially improved is the readability of nameStartsWith ('A')
and zipStartsWith ('8')
Upvotes: 2