jorgen
jorgen

Reputation: 3593

Mapping a curried filter using lodash functions

I want to make a function that, given a list of predicates, produces an array filter.

Using lodash, I define:

const { curryRight, filter, flowRight } = require('lodash');

const curriedFilter = curryRight(filter);
const filterFromPredicates = (predicateList) => flowRight(...predicateList.map(curriedFilter));

But this gives the wrong answer:

const filter = filterFromPredicates([(x) => x > 2, (x) => x > 4])
filter([1,2,3,4,5,6,7,8,9]) // -> [1,2,3,4,5,6,7,8,9]

where I would expect [5,6,7,8,9]

However defining the curried filter as follows works:

const curriedFilter = (predicate) => (array) => array.filter(predicate);

Am I misunderstanding the usage of curryRight?

Upvotes: 4

Views: 504

Answers (2)

Ori Drori
Ori Drori

Reputation: 191976

Your code doesn't work because the map passes 3 arguments to the curriedFilter - the predicate, the index, and the original array. You'll need to pass only the predicate to curriedFilter.

const { curryRight, filter, flow, ary } = _;

const curriedFilter = ary(curryRight(filter), 1);

const filterFromPredicates = (predicateList) => flow(...predicateList.map(curriedFilter)); // limit the number of params passed to curriedFilter

const fn = filterFromPredicates([x => x > 2, x => x < 6]); // don't override the filter you've imported

const result = fn([1,2,3,4,5,6,7,8,9]); // -> [3, 4, 5]

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Another option would be to limit the number of parameters curriedFilter accepts with _.ary():

const { curryRight, filter, flow, ary } = _;

const curriedFilter = ary(curryRight(filter), 1);

const filterFromPredicates = (predicateList) => flow(...predicateList.map(curriedFilter)); // limit the number of params passed to curriedFilter

const fn = filterFromPredicates([x => x > 2, x => x < 6]); // don't override the filter you've imported

const result = fn([1,2,3,4,5,6,7,8,9]); // -> [3, 4, 5]

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

You can use _.overEvery() to generate the predicate you'll pass to the curried filter:

const { flow, overEvery, curryRight, filter } = _;

const filterFromPredicates = flow(overEvery, curryRight(filter));

const fn = filterFromPredicates(x => x > 2, x => x < 6); // don't override the filter you've imported

const result = fn([1,2,3,4,5,6,7,8,9]); // -> [3, 4, 5]

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

If you use lodash/fp you want need the _.curryRight() since the functions are auto-curried, and the parameters are iteratee-first data-last:

const { flow, overEvery, filter } = _;

const filterFromPredicates = flow(overEvery, filter);

const fn = filterFromPredicates([x => x > 2, x => x < 6]); // don't override the filter you've imported

const result = fn([1,2,3,4,5,6,7,8,9]); // -> [3, 4, 5]

console.log(result);
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>

Upvotes: 3

jstuartmilne
jstuartmilne

Reputation: 4488

How about this:

function filterFromPredicates(predicates) {
 return (...args) => predicates
  .map(predfn=> predfn(...args))
  .every(a=>a)
}
[1,2,3,4,5,6,7,8,9].filter(filterFromPredicates([(x) => x > 2, (x) => x > 4]))

if you want everything in the same function then :

function filterFromPredicates(predicates) {
     return (someArray)=> someArray.filter((...args) => predicates
      .map(predfn=> predfn(...args))
      .every(a=>a))
    }

const filter = filterFromPredicates([(x) => x > 2, (x) => x > 4])
filter([1,2,3,4,5,6,7,8,9])

That will return as you wanted ^

Upvotes: 1

Related Questions