Mapping a curried filter using lodash functions - javascript

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?

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>

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 ^

Related

How to get the excluded elements when using javascript's filter

Javascript's filter returns the array with all the elements passing the test.
How how can you easily get all the elements that failed the test without running the test again, but for the converse? How is the best way to do it, even if you have to run the test again.
let arr; // this is the array on which the filter will be run [SET ELSEWHERE]
let fn; // The filter function [SET ELSEWHERE]
let goodElements; // This will be the new array of the good elements passing the test
let badElements; // This will be the new array of the elements failing the test
goodElements = arr.filter(fn);
// SO HOW IS badElements set????
How is badElements set?
If you don't want to do two iterations, you can use a for loop and a ternary operator:
let arr = [1, 2, 3];
let fn = (e) => e % 2 == 0;
let goodElements = [];
let badElements = [];
for(const e of arr) (fn(e) ? goodElements : badElements).push(e);
console.log(goodElements);
console.log(badElements);
Otherwise, just invert the condition with the ! operator:
let arr = [1, 2, 3];
let fn = (e) => e % 2 == 0;
let goodElements;
let badElements;
goodElements = arr.filter(fn);
badElements = arr.filter(e => !fn(e));
console.log(goodElements);
console.log(badElements);
Don't use filter(). If you want to partition the data into two arrays, do it yourself.
function partition(array, fn) {
let goodArray = [],
badArray = [];
array.forEach(el => {
if (fn(el)) {
goodArray.push(el);
} else {
badArray.push(el);
}
});
return [goodArray, badArray];
}
let [goodElemements, badElements] = partition(arr, fn);
You could also use reduce()
function partition(array, fn) {
return array.reduce(acc, el => {
if (fn(el)) {
acc[0].push(el);
} else {
acc[1].push(el);
}
}, [[],[]]);
}
let [goodElemements, badElements] = partition(arr, fn);
If you want to do this strictly with Array.filter and only one loop, then consider something like this:
UPD: based on #AlvaroFlañoLarrondo comment, added external condition function to current approach.
// Array of elements
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
// External filter method
const fn = e => e.length > 6;
// Define empty bad array
const bad = [];
// Define good array as result from filter function
const good = words.filter(word => {
// In filter condition return good values
if(fn(word)) return word;
// And skip else values by just pushing them
// to bad array, without returning
else bad.push(word);
});
// Results
console.log(good);
console.log(bad);
You could use reduce in order to split the array into two arrays based on a predicate, like in this example:
const arr = [1, 0, true, false, "", "foo"];
const fn = element => !element;
const [goodElements, badElements] = arr.reduce(
([truthies, falsies], cur) =>
fn(cur) ? [truthies, [...falsies, cur]] : [[...truthies, cur], falsies],
[[], []]
);
console.log(goodElements, badElements);
I see two possible routes:
// in this case, if it's not in `goodElements`, it's a bad 'un
badElements = arr.filter( el => !goodElements.includes(el) );
or this:
// we don't **know** if fn needs the optional parameters, so we will
// simply pass them. If it doesn't need 'em, they'll be ignored.
badElements = arr.filter( (el, idx, arr) => !fn(el, idx, arr) );
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const fn = (x) => x % 2 === 0
const removeItems = (array, itemsToRemove) => {
return array.filter(v => {
return !itemsToRemove.includes(v);
});
}
const goodElements = arr.filter(fn)
console.log(goodElements) // [ 0, 2, 4, 6, 8 ]
const badElements = removeItems(arr, goodElements)
console.log(badElements) // [ 1, 3, 5, 7, 9 ]

Add console log in an one arrow (auto return) function without adding curly braces

So consider you have a one line auto return arrow function:
const test = (val) => val;
How would you check the val without doing:
const test = (val) => {
console.log(val);
return val;
};
You can actually use || between the arrow function and the returned value like so:
const test = (val) => console.log(val) || val;
This will not interfere with the process and will also log val without the hassle of adding {} and return and undoing it once you're done
echo is a great function for this -
const echo = (x) =>
( console.log(x)
, x
)
const test = x => echo(x) + 1
const arr = [ 1, 2, 3 ]
const result = arr.map(test)
console.log(result)
Arrow functions are a functional style. Utilising functional techniques will make it more enjoyable to work with arrow expressions -
const effect = f => x =>
(f(x), x)
const echo =
effect(console.log)
const test = x => echo(x + 1) // <-- wrap echo around anything
const arr = [ 1, 2, 3 ]
const result = arr.map(test)
console.log(result)
// 2
// 3
// 4
// [ 2, 3, 4 ]
You can use echof to wrap an entire function, such as echof(test) -
const effect = f => x =>
(f(x), x)
const comp = (f, g) =>
x => f(g(x))
const echo =
effect(console.log)
const echof = f =>
comp(echo, f)
const test = x => x * x
const arr = [ 1, 2, 3 ]
const result = arr.map(echof(test)) // <-- echof
console.log(result)
// 1
// 4
// 9
// [ 1, 4, 9 ]

Can you mutate the properties of a factory function without using this?

I've been reading about the benefits of using factory functions over classes, however, I'm having trouble getting them to work in a similar manner.
For instance, with classes:
class NumberManager {
constructor(numbers){
this.numbers = numbers;
}
removeNumber(input){
this.numbers = this.numbers.filter( number => number != input )
}
}
n = new NumberManager([1, 2])
n.numbers // --> [1, 2]
n.removeNumber(2)
n.numbers // --> [1]
However, with a factory function:
const NumberFactory = (numbers) => {
const removeNumber = (input) => {
numbers = numbers.filter( number => number != input )
}
return {
numbers,
removeNumber
}
}
n = NumberFactory([1, 2])
n.numbers // --> [1, 2]
n.removeNumber(2)
n.numbers // --> [1, 2]
Is it possible to have a similar level of freedom that classes have (e.g. easily mutating properties), without having to use things like this (which can break across execution contexts) and new (which can break if having to convert a class to a factory)?
Put the object you want to return from the factory into a variable before returning it, then you can reference that variable and mutate the object:
const NumberFactory = (numbers) => {
const removeNumber = (input) => {
instance.numbers = instance.numbers.filter( number => number != input );
};
const instance = { numbers, removeNumber };
return instance;
}
const n = NumberFactory([1, 2]);
console.log(n.numbers); // --> [1, 2]
n.removeNumber(2);
console.log(n.numbers); // --> [1]
Besides #CertainPerformance's solution, you can expose a getter (and maybe a setter as well) for the numbers varible:
const NumberFactory = (numbers) => {
const removeNumber = (input) => {
numbers = numbers.filter( number => number != input );
};
return {
get numbers(){ return numbers },
// And if you want to allow external replacement of numbers:
// set numbers(value){ numbers = value },
removeNumber
};
}
const n = NumberFactory([1, 2]);
console.log(n.numbers); // --> [1, 2]
n.removeNumber(2);
console.log(n.numbers); // --> [1]
Or just mutate the array (using mutator methods instead of filter):
const NumberFactory = (numbers) => {
const removeNumber = (input) => {
for(let i = 0; i < numbers.length; ){ // <-- Note that we don't increment i here
if(numbers[i] != input) // <-- Condition goes here
i++
else
numbers.splice(i, 1)
}
};
return {
numbers,
removeNumber
};
}
const n = NumberFactory([1, 2]);
console.log(n.numbers); // --> [1, 2]
n.removeNumber(2);
console.log(n.numbers); // --> [1]

Count the repetition of an element in an array using a function with one parameter

Good Day, I am trying to count how many times a particular element in an array appears. I tried but my code below counts only one of the array even if it appears more than once (this is not the problem). I want it to return the amount of time each element appears. For example
let arr = [1, 3, 2, 1];
this should return
{1:2} {3:1} {2:1}
My code returns 3 (as in it just doesn't count one twice)
How do i go about this?
Below is my code
function numberCount(number) {
let count = 0;
number.forEach(function (item, index) {
if (number.indexOf(item) == index) count++;
});
console.log(count);
}
While iterating over number (better to call it arr, it's an array, not a number), use an object to keep track of the number of times each number has occured so far. Then, iterate over the resulting object's entries to create the objects desired:
let arr = [1, 3, 2, 1];
function numberCount(arr) {
let count = 0;
const obj = arr.reduce((a, num) => {
a[num] = (a[num] || 0) + 1;
return a;
}, {});
return Object.entries(obj).map(([key, val]) => ({ [key]: val }));
}
console.log(numberCount(arr));
Numeric keys always come in numeric order in an object. If you want the objects in the output to come in insertion order (eg, the object with key 3 before the object with key 2), then use a Map instead of an object (map keys will be iterated over in insertion order):
let arr = [1, 3, 2, 1];
function numberCount(arr) {
let count = 0;
const map = arr.reduce((a, num) => (
a.set(num, (a.get(num) || 0) + 1)
), new Map());
return [...map.entries()]
.map(([key, val]) => ({ [key]: val }));
}
console.log(numberCount(arr));
You should filter out these numbers, then use the length:
let arr = [1, 3, 2, 1];
function itemCount(array) {
var sorted = array.sort()
var uniqueCount = sorted.filter((v, i, a) => a.indexOf(v) == i);
var count = [];
uniqueCount.forEach(item => {
var itemCount = sorted.filter(e => e == item).length;
count.push({[item]: itemCount});
});
return count;
}
console.log(itemCount(arr));
I would suggest not reinventing the wheel, and instead use lodash which already has this function. Using countBy() you will get an object you can then convert into your desired result. For example:
const arr = [1, 3, 2, 1]
const count = _.countBy(arr)
const result = Object.keys(count).map(k => ({ [k]: count[k] }))
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>

An array mapping function in lodash

I have this chunk of javascript code:
function arrayMapper(mappingFunc) {
return items => items.map(mappingFunc);
}
function fooTransformer(tem) {
return (...); // do something with item and returns a value
}
function barTransformer(tem) {
return (...); // do something with item and returns a value
}
const foosTransformer = arrayMapper(fooTransformer);
const barsTransformer = arrayMapper(barTransformer);
(...)
foosTransformer([1, 2, 3]);
I was wondering if something like my arrayMapper function would exist natively in something like lodash, just to avoid reinventing the wheel.
What you're essentially doing is currying the map function (from the right).
Lodash allows you do to just that:
const mapper = _.curryRight(_.map);
const square = mapper(x => x ** 2);
console.log(
square([1, 2, 3]) // [1, 4, 9]
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
That being said, it's certainly an overkill to bring in a whole library for such a small feature. Maybe if you're using npm, you could just npm install lodash.curryright lodash.map and use only those but that's still likely to be unnecessary.
You could use chain method and then you can add map multiple times and at the end call value()
var arr = [1, 2, 3];
var add1 = e => e + 1;
var add2 = e => e + 2;
var result = _.chain(arr).map(add1).map(add2).value()
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
You could also use ES6 parameter destructuring and reduce() to create function that takes array and any number of callback function and call map method on that array with each function you provided.
var arr = [1, 2, 3];
var add1 = e => e + 1;
var add2 = e => e + 2;
function arrayMapper(arr, ...mappingFunc) {
return mappingFunc.reduce((r, f) => (r = Array.prototype.map.call(r, f), r), arr)
}
var result = arrayMapper(arr, add1, add2)
console.log(result)

Categories