Lodash filter and functional composition - javascript

I have an array of objects with a media key and the value that may or may be not empty. I have a function to pick that key value and a function that checks that the value is empty so that mediaEmptyComparer({media: ''}) returns true and mediaEmptyComparer({media: 'somevalue'}) is false.
var array = _.times(100, function () { return {media: Math.random() < 0.5 ? '' : 'value'} });
var mediaPicker = _.partialRight(_.get, 'media');
var mediaEmptyComparer = _.flow(mediaPicker, _.isEmpty);
var splittedArray = _.filter(array, mediaEmptyComparer);
console.log(splittedArray);
When I try to use such function as the predicate for _.filter, it always fails (as in the example) but if I write:
var splittedArray = _.filter(array, function (x) { return mediaEmptyComparer(x);});
it works. It's like if the value for each iteration is not passed as argument to the function. Any help? Thanks

Predicate of _.filter() method is invoked with three arguments: value, index|key and collection.
Use an _.ary() method to cap the number of arguments accepted by a method.
https://lodash.com/docs#ary
var splittedArray = _.filter(array, _.ary(mediaEmptyComparer, 1));
For more information check this:
https://github.com/lodash/lodash/issues/844
Good luck!

Related

Check if a word matches another in a map function in JS [duplicate]

I would like to filter an array of items by using the map() function. Here is a code snippet:
var filteredItems = items.map(function(item)
{
if( ...some condition... )
{
return item;
}
});
The problem is that filtered out items still uses space in the array and I would like to completely wipe them out.
Any idea?
EDIT: Thanks, I forgot about filter(), what I wanted is actually a filter() then a map().
EDIT2: Thanks for pointing that map() and filter() are not implemented in all browsers, although my specific code was not intended to run in a browser.
You should use the filter method rather than map unless you want to mutate the items in the array, in addition to filtering.
eg.
var filteredItems = items.filter(function(item)
{
return ...some condition...;
});
[Edit: Of course you could always do sourceArray.filter(...).map(...) to both filter and mutate]
Inspired by writing this answer, I ended up later expanding and writing a blog post going over this in careful detail. I recommend checking that out if you want to develop a deeper understanding of how to think about this problem--I try to explain it piece by piece, and also give a JSperf comparison at the end, going over speed considerations.
That said, **The tl;dr is this:
To accomplish what you're asking for (filtering and mapping within one function call), you would use Array.reduce()**.
However, the more readable and (less importantly) usually significantly faster2 approach is to just use filter and map chained together:
[1,2,3].filter(num => num > 2).map(num => num * 2)
What follows is a description of how Array.reduce() works, and how it can be used to accomplish filter and map in one iteration. Again, if this is too condensed, I highly recommend seeing the blog post linked above, which is a much more friendly intro with clear examples and progression.
You give reduce an argument that is a (usually anonymous) function.
That anonymous function takes two parameters--one (like the anonymous functions passed in to map/filter/forEach) is the iteratee to be operated on. There is another argument for the anonymous function passed to reduce, however, that those functions do not accept, and that is the value that will be passed along between function calls, often referred to as the memo.
Note that while Array.filter() takes only one argument (a function), Array.reduce() also takes an important (though optional) second argument: an initial value for 'memo' that will be passed into that anonymous function as its first argument, and subsequently can be mutated and passed along between function calls. (If it is not supplied, then 'memo' in the first anonymous function call will by default be the first iteratee, and the 'iteratee' argument will actually be the second value in the array)
In our case, we'll pass in an empty array to start, and then choose whether to inject our iteratee into our array or not based on our function--this is the filtering process.
Finally, we'll return our 'array in progress' on each anonymous function call, and reduce will take that return value and pass it as an argument (called memo) to its next function call.
This allows filter and map to happen in one iteration, cutting down our number of required iterations in half--just doing twice as much work each iteration, though, so nothing is really saved other than function calls, which are not so expensive in javascript.
For a more complete explanation, refer to MDN docs (or to my post referenced at the beginning of this answer).
Basic example of a Reduce call:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
// if condition is our filter
if (iteratee > 1) {
// what happens inside the filter is the map
memo.push(iteratee * 2);
}
// this return value will be passed in as the 'memo' argument
// to the next call of this function, and this function will have
// every element passed into it at some point.
return memo;
}, initialMemo)
console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
more succinct version:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Notice that the first iteratee was not greater than one, and so was filtered. Also note the initialMemo, named just to make its existence clear and draw attention to it. Once again, it is passed in as 'memo' to the first anonymous function call, and then the returned value of the anonymous function is passed in as the 'memo' argument to the next function.
Another example of the classic use case for memo would be returning the smallest or largest number in an array. Example:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.
An example of how to write your own reduce function (this often helps understanding functions like these, I find):
test_arr = [];
// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
// if we did not pass in a second argument, then our first memo value
// will be whatever is in index zero. (Otherwise, it will
// be that second argument.)
const initialMemoIsIndexZero = arguments.length < 2;
// here we use that logic to set the memo value accordingly.
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
// here we use that same boolean to decide whether the first
// value we pass in as iteratee is either the first or second
// element
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
// memo is either the argument passed in above, or the
// first item in the list. initialIteratee is either the
// first item in the list, or the second item in the list.
memo = reduceFunc(memo, this[i]);
// or, more technically complete, give access to base array
// and index to the reducer as well:
// memo = reduceFunc(memo, this[i], i, this);
}
// after we've compressed the array into a single value,
// we return it.
return memo;
}
The real implementation allows access to things like the index, for example, but I hope this helps you get an uncomplicated feel for the gist of it.
That's not what map does. You really want Array.filter. Or if you really want to remove the elements from the original list, you're going to need to do it imperatively with a for loop.
Array Filter method
var arr = [1, 2, 3]
// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })
// ES2015 syntax
arr = arr.filter(item => item != 3)
console.log( arr )
You must note however that the Array.filter is not supported in all browser so, you must to prototyped:
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /*, thisp*/)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var res = new Array();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
{
var val = this[i]; // in case fun mutates this
if (fun.call(thisp, val, i, this))
res.push(val);
}
}
return res;
};
}
And doing so, you can prototype any method you may need.
TLDR: Use map (returning undefined when needed) and then filter.
First, I believe that a map + filter function is useful since you don't want to repeat a computation in both. Swift originally called this function flatMap but then renamed it to compactMap.
For example, if we don't have a compactMap function, we might end up with computation defined twice:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.filter(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
return isIncluded;
})
.map(x => {
let computation = x / 2 + 1;
return `${x} is included because ${computation} is even`
})
// Output: [2 is included because 2 is even, 6 is included because 4 is even]
Thus compactMap would be useful to reduce duplicate code.
A really simple way to do something similar to compactMap is to:
Map on real values or undefined.
Filter out all the undefined values.
This of course relies on you never needing to return undefined values as part of your original map function.
Example:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.map(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
if (isIncluded) {
return `${x} is included because ${computation} is even`
} else {
return undefined
}
})
.filter(x => typeof x !== "undefined")
I just wrote array intersection that correctly handles also duplicates
https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0
// array intersection that correctly handles also duplicates
const intersection = (a1, a2) => {
const cnt = new Map();
a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
return a1.filter(el => el in cnt && 0 < cnt[el]--);
};
const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]
First you can use map and with chaining you can use filter
state.map(item => {
if(item.id === action.item.id){
return {
id : action.item.id,
name : item.name,
price: item.price,
quantity : item.quantity-1
}
}else{
return item;
}
}).filter(item => {
if(item.quantity <= 0){
return false;
}else{
return true;
}
});
following statement cleans object using map function.
var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);

Explain API function signature syntax [duplicate]

This question already has answers here:
How to interpret function parameters in software and language documentation?
(4 answers)
Closed 2 years ago.
Reading documentation for a few years I've often been confused by the syntax commonly used in explaining function signatures. for instance:
From the Mozilla Array.map Docs:
var new_array = arr.map(callback[, thisArg])
The docs list three arguments for the callback: currentValue, index, and array, but the signature just has this weird callback[, thisArg] syntax. Whats the deal with that comma? why are there array brackets next to 'callback'? Is there any documentation out there on this syntax? is there a name for this syntax? any help would be appreciated.
Thanks!
The function Array.prototype.map expects a function as the first argument:
var new_array = arr.map(callback[, thisArg])
The square brackets indicate that the secondparameter is optional. You can call Array.prototype.map with or without a second argument. Both functions calls are valid:
var array = [1, 2, 3, 4];
var myFunc = function (number) {
return number * 5;
};
var myFuncUsingThis = function (number) {
console.log(this);
return number;
};
var myThisArg = {
foo: 'bar'
};
console.log(array.map(myFunc));
console.log(array.map(myFuncUsingThis, myThisArg));
The last is the same as
console.log(array.map(myFuncUsingThis.bind(myThisArg)));
So, if the function that you provide to Array.prototype.map uses the this object, you can specify the this object of the function when it is called by Array.prototype.map by using the second (optional) argument.
currentValue, index and array are something completely different. You do not have to provide them when you call Array.prototype.map. Instead, Array.prototype.map provides them for you: it calls the function that you gave it with those three arguments (but you don't have to use all three arguments).
The first argument to your function is the value of the element in the array that is currently processed, the second argument is the index of that element, and the third argument is the array itself.
You can write a function that takes use of the index parameter:
var array = Array(20).fill(0); // an array containing 20 zeros
var evenNumbers = array.map(function (number, index) {
// number is zero, index is the index of the element we should transform
return index * 2;
});
console.log(evenNumbers);
Maybe it helps if you take a look at a (naïve) implementation of Array.prototype.map:
Array.prototype.map = function (callback, thisArg) {
// "this" is the array on which we work
var arr = []; // the new array that we build
var result;
for (var i = 0; i < this.length; i += 1; i++) {
result = callback.call(thisArg, this[i], i, this);
arr[i] = result;
}
return arr;
};
The parameters inside the brackets mean they are optional.

Javascript, looping through an array and the arguments "object"

I can't get my head around this, i'm using filter to loop through an array and filter out all the integers passed as arguments ,i'm not limited in the number of arguments.
But i'm stuck here when it's about to get back to the function the value of the arguments object, at least more that once.
In my code below, obviously it's not fully working because i'm doing a return within the for…in loop, this is where i don't get how I can get the this second loop without having i re-initialised to 0…
function destroyer(arr) {
var args = arguments.length;
var arg0 = arguments[0];
var Nargs = Array.prototype.slice.call(arguments, 1);
var newArr = [];
newArr = arg0.filter(filtre);
/*Only the first argument is
filtered out,here it's the value "2",
but should filter out [2,3].
The expected result is [1,1]*/
console.log(newArr);
return newArr;
function filtre(e){
for (var i in Nargs){
return e !== Nargs[i];
}
}
}
destroyer([1, 2, 3, 1, 2, 3], 2,3);
Hope this is clear enough,
Thanks for any input !
Matth.
While rgthree provided a solution very similar to what you already had, I wanted to provide a solution that takes advantage of newer ES6 features for modern browsers. If you run this through Babel, it will result in essentially the same code as in that solution, though it will still require a shim for the includes call.
function destroyer(source, ...args) {
return source.filter(el => !args.includes(el));
}
Explanation:
...args uses the spread operator to get all but the first argument into an array named args.
We can directly return the filtered array, assuming you don't need to log it and that was just for debugging.
el => !args.includes(el) is an anonymous arrow function. Since no braces are used, it will automatically return the result of the expression.
Since args is an array, we can directly use Array.prototype.includes to check if the current element is in the arguments to be removed. If it exists, we want to remove it and thus invert the return with !. An alternative could be the following: el => args.includes(el) == false.
Since your return statement in your filtre function is always executed on the first run, it's only returning the whether the first number in Nargs is equal to the current item. Instead, you can use indexOf for full browser support (which returns the index of the item in an array, or "-1" if it's not in an array) instead of a loop (or includes, or a loop, etc. as shown further below):
function destroyer(arr) {
var args = arguments.length;
var arg0 = arguments[0];
var Nargs = Array.prototype.slice.call(arguments, 1);
var newArr = [];
function filtre(e){
return Nargs.indexOf(e) === -1;
}
newArr = arg0.filter(filtre);
console.log(newArr);
return newArr;
}
destroyer([1, 2, 3, 1, 2, 3], 2,3);
If you don't need Internet Explorer support, you could use Array.includes:
function filtre(e){
return !Nargs.includes(e);
}
And finally, if you really wanted to use a loop, you would only want to filter an item (return false) once it's found otherwise allow it (return true):
function filtre(e){
for (var i = 0, l = Nargs.length; i < l; i++) {
if (e === Nargs[i])
return false;
}
return true;
}

How to use apply with currying?

I have code that is using currying to get the average on an array that results from concatenating two arrays: an n size array and an m size array.
var avg = function(...n){
let tot=0;
for(let i=0; i<n.length; i++){
tot += n[i];
}
return tot/n.length;
};
var spiceUp = function(fn, ...n){
return function(...m){
return fn.apply(this, n.concat(m));
}
};
var doAvg = spiceUp(avg, 1,2,3);
console.log(doAvg(4,5,6));
In this line return fn.apply(this, n.concat(m));, I don't understand why do we need to use apply. What is the object we are binding with the average function and why does just normal calling (return fn(n.concat(m));) not work?
In that example, this is not that important. It would also work if instead of this you would pass an empty object instead. It's just an example on how to use apply.
What you need to focus is on the second parameter n.concat(m). They key concept here is that passing an array as a second argument you are calling that function (fn) passing each value in the array as an argument.
About your second question: no, it won't work because fn expects several arguments (one per value to calculate the average) while by doing return fn(n.concat(m)); you are just passing one argument, an array containing all values
Maybe you would understand it better with a simpler example:
function sum3params(a,b,c){
return a+b+c;
}
console.log(sum3params([3,4,2])) // won't work
console.log(sum3params.apply(this, [3,4,2])) // will work ('this' is not important here)
For this use case, it does not. But consider the following:
var foo = {
bar: 3
};
var addBar = function(a, b) { return a + b + this.bar };
foo.add3AndBar = spiceUp(addBar, 3);
foo.add3AndBar(1); // 7
Using apply means that your spiceUp function can be applied to methods as well as normal functions. For more likely example, consider partially applying when defining a method on a prototype:
const ENV = "linux";
DoesSomePlatformSpecificStuff.prototype.getPath = spiceUp(ENV);
apply also will spread the gathered array of arguments back out into positional arguments which can also be done like so:
return fn(...n.concat(m));
Which can be simplified as
return fn(...n, ...m);
Which is equivalent to
return fn.apply(undefined, n.concat(m));

Best way of basically doing a `where` clause in Javascript?

I'm trying to parse some JSON that is sent to me and it's all in the format of
[{key:value},{key2:value2}, ... ]
What would be the best way to get the value of key2 in this? Is there a way to do it without doing a for loop?
You could use the Select function from the Underscore.js library.
Not really, but it wouldn't be hard to create a function to do that. However, it would indeed involves a for loop.
For the sake of completion, that would be the function:
function selectWhere(data, propertyName) {
for (var i = 0; i < data.length; i++) {
if (data[i][propertyName] !== null) return data[i][propertyName];
}
return null;
}
Usage:
var key2value = selectWhere(data, "key2");
Javascript Array comes with methods that do just what you are asking for - find entries without you having to code a for-loop.
You provide them with the condition that you want. A compact and convenient way to do that is with an arrow (or "lambda") function. In your case, you are looking for array entries that have a specific key, so the arrow function could look something like this:
e => e.hasOwnProperty("key2")
Following the lead of some of the others, let's start with the assumption
var arr = [{key:"value"}, {key2:"value2"}, {key3:"value3"}]
If you expect that at most one member of the array has the key you want, you can use the find() function. It will test each array member until it finds one where your condition is true, and return it. If none are true, you'll get undefined.
var foundentry = arr.find(e => e.hasOwnProperty("key2"))
Either foundentry will be undefined or it will be the {key2:"value2"} that you are looking for, and can extract value2 from it.
If arr can have more than one entry with the key that you are looking for, then instead of find() use filter(). It gives back an array of entries that meet your criteria.
var foundarray = arr.filter(e => e.hasOwnProperty("key2"))
jQuery grep() is a good analog for a Where clause:
var array = [{key:1},{key:2}, {key:3}, {key:4}, {key:5}];
var filtered = jQuery.grep(array, function( item, index ) {
return ( item.key !== 4 && index > 1 );
});
Your filtered array will then contain two elements,
[{key:3}, {key:5}]
You can't do it with an array, but you can make an associative array like object with it. Once you make it, you can use it like hash.
var arr = [{key:value},{key2:value2}, ... ], obj = {};
for (var i = 0, len = arr.length; i < len; i++) {
$.extend(obj, arr[i]);
}
console.log(obj.key2); // value2
Here's an example that prototype's the Array object. Note: this is shown for example - find is not a good name for this function, and this probably will not be needed for all arrays
Instead, consider just using the function definition and creating a function like getObjVal, calling like getObjVal(arr,'propName'), similar to LaurenT's answer.
Given
var arr = [{key:'value'},{key2:'value2'}];
Definition
// for-loop example
Array.prototype.find = function (prop){
for(var i=this.length; i--; )
if (typeof this[i][prop] !== 'undefined')
return this[i][prop];
return undefined;
}
// for-each loop example
Array.prototype.find = function (prop){
for (var i in this)
if ( this.hasOwnProperty(i) && typeof this[i][prop] !== "undefined" )
return this[i][prop];
return undefined;
}
Usage
console.log( arr.find('key2') ); // 'value2'
console.log( arr.find('key3') ); // undefined
Use .filter() method for this object array, for example in your case:
var objArray = [{key:"Hello"},{key2:"Welcome"} ];
var key2Value=objArray.filter(x=>x.key2)[0].key2;
Regex - no for loop:
var key2Val = jsonString.match(/\{key2:[^\}]+(?=\})/)[0].substring("{key2:".length);
Top answer does the job. Here's a one liner version of it using lodash (same as underscore for the most part):
var result = _.filter(data, _.partialRight(_.has, 'key2'));
In lodash, select is just an alias for filter. I pass it the data array filled with objects. I use _.has as the the filter function since it does exactly what we want: check if a property exists.
_.has expects two args:
_.has(object, path)
Since _.has expects two arguments, and I know one of them is always constant (the path argument). I use the _.partialRight function to append the constant key2. _.partialRight returns a new function that expects one argument: the object to inspect. The new function checks if obj.key2 exists.
Heyas. You can use the lodash library's .reduce() or .transform() functions to implement this. Lodash is more modular than underscore (Underscore around 5kb, Lodash around 17kb), but is generally lighter because you only include the specific modules you need
(please see: https://news.ycombinator.com/item?id=9078590 for discussion). For this demonstration I will import the entire module (generally not an issue on the backend):
I wrote these snippets for either scenario which handle both numeric and non-numeric arguments.
https://lodash.com/docs#reduce
https://lodash.com/docs#transform
Pull in lodash:
var _ = require('lodash');
_.reduce() to where clause:
var delim = ' WHERE ', where = _.isEmpty(options) ? '' : _.reduce(options, function(r, v, k) {
var w = r + delim + k + '=' + (_.isNumber(v) ? v : ("'" + v + "'"));
delim = ' AND ';
return w;
}, '');
_.transform() to where clause:
var where = _.isEmpty(options) ? '' : ' WHERE ', delim = '';
_.transform(options, function(r, v, k) {
where = where + delim + k + '=' + (_.isNumber(v) ? v : ("'" + v + "'"));
delim = ' AND ';
});
Hope that helps.
Try this:
var parsedJSON = JSON.parse(stringJSON);
var value = parsedJSON['key2'];

Categories

Resources