I'm working on a course and I'm to return an array using .filter from an array of strings.
arr = ['tim','tom','taaaaaamy'];
const validUserNames = arr.filter(n => n.length < 10);
console.log(validUserNames);
While this works, the course does not let me use a global variable. How would I write this without declaring the array initially? Thanks.
This should suffice your requirements:
const validUserNames = (arr) => arr.filter(n => n.length < 10);
validUserNames(['tim','tom','taaaaaamy'])
Keep in mind that all values in your array actually have less then 10 characters.
Since the goal is to create a function "validUserNames" that can be used on some array of strings, returning an array of only the valid strings, you need a function that accepts a parameter, which is the array that the caller wants filtered.
The traditional/classic format declares and names the parameters in the function statement:
function validUserNames(names) {
...
}
In the functional style as in your attempt, using an arrow function the parameter is the left side of the arrow function declaration
const validUserNames = (names) => { the function body };
Then you use the parameter "names" in the place where your global "arr" is used.
As noted in the linked documentation, the parentheses around the parameter are not strictly necessary (depending on the arguments) so your function your wrote with the addition of a parameter to take the place of the global becomes this:
const validUserNames = names => names.filter(s => s.length < 10);
console.log( validUserNames(['tim','tom','taaaaaaxxxxxxmy']) );
console.log( validUserNames(['verylongname', 'fred', 'anothertoolongname', 'jeff']) );
Related
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);
Pandas Dataframe's query method can refer to variables in the environment by prefixing them with an ‘#’ character like #a, ex:
seq = [1,2,3,4]
df.query('col1.isin(#seq)')
The method df.query can access the calling context and assign the seq variable value to #seq. I want to add this function in JavaScript, but how to access the function calling context in JavaScript?
Pandas is built on Python, which is different from JavaScript. (You knew that, of course, but... 🙂)
If we had code like your example in JavaScript, there would be no way for the query function to access the value of the seq local variable (I'm assuming it's local, not global). It just has no way to reach it.
If we wanted similar functionality, we'd probably use a tagged template function:
seq = [1,2,3,4];
df.query`col1.isin(${seq})`;
(That's a tagged function call, one of two/three kinds of function calls in JavaScript that don't use ().)
That creates a template object and calls df.query with that template object and an argument for each substitution (${...}) in the template. The function still doesn't reach out and get it, it's the template literal (the bit in backticks) that gets the value, which is then passed to the function.
Just for illustration:
function logArray(label, array) {
console.log(label + ":");
array.forEach((element, index) => {
console.log(` ${index}: ${JSON.stringify(element)}`);
});
}
const df = {
query(template, ...args) {
logArray("template", template);
logArray("args", args);
}
};
const seq = [1,2,3,4];
df.query`col1.isin(${seq})`;
Note that what gets passed to the function is the actual array, not a string version of it, so we can use that array to see if col1 is in [1,2,3,4], like this:
const df = {
col1: 2,
query(template, ...args) {
// (You'd need to have some parser here for the DSL aspects
// of your query; this is just a hardcoded example)
if (template[1] === ".isin(") {
// In `${"col1"}.isin(${seq})`, args[0] will be the
// string "col1" and args[1] will be the array from
// `seq`
return args[1].includes(this[args[0]]);
} else {
throw new Error(`Unknown expression`);
}
}
};
let seq = [1,2,3,4];
let result = df.query`${"col1"}.isin(${seq})`;
console.log(result); // true, because df.col1 is 2 and that's
// in [1,2,3,4]
seq = [5,6,7,8];
result = df.query`${"col1"}.isin(${seq})`;
console.log(result); // false, because df.col1 is 2 and that's not
// in [5,6,7,8]
I'm working on a Javascript challenge. The challenge is the code below in which you add the numbers provided together:
function addTogether() {
//I left this blank because I'm focused on checking for the second parentheses
}
However, if you tried to pass this addTogether(5)(7) to the function addTogether(), it would be difficult to get the second parentheses.
However, you can get both parentheses if you write this code:
function addTogether() {
//get first parenteses
let first = arguments;
//get second parentheses
let second = function() {
return arguments;
};
}
But my question is how do you check if there's a second parentheses? Because I'm passing other data such as addTogether(2,3). In other words, I might be passing some with one parentheses and another with two parentheses to the function addTogether()
There is no way to check, unless you're parsing the source code, but your function can optionally accept second argument and if it's provided return the sum, otherwise return a function
const addTogether = (a, opt) => opt ? a + opt : b => a + b
console.log("addTogether(1,3) => ", addTogether(1,3))
console.log("addTogether(4)(6) => ", addTogether(4)(6))
Naive implementation using function that coerces to number.
Using reduce to calculate sums. Overriding ##toPrimitive to create function that coerces to value of add. Recursive call to generate recursively chainable callable number coercable functions.
Check ECMAScript specs if you want to customize behavior further. You will need to understand the conversion path.
function sum(...args) {
const first = args.reduce((sum,x)=>sum+x,0)
const second = function(...args) {return sum(first, ...args)}
second[Symbol.toPrimitive] = function(){return first}
return second
}
console.log(
1+sum(1)(2)+sum(1)+sum(3),
sum(1,1,2) (1,3)(0)(0)+sum(0,-1,1,0),
sum(1)(1)(2)(1)(3)(0)(0)(-1,1)(0)+sum()
)
I'm trying to write my own higher order function right now and I want to know how functions like map() and reduce() access the array they are being applied to. And not just for arrays either, but with any higher order function like toString() or toLowerCase().
array.map()
^^^ // How do I get this data when I am writing my own higher order function?
array.myOwnFunction(/* data??? */)
I hope this makes sense. I'm sure the answer is out there already, but I'm struggling to know what to search for to find the information.
You can add it to the Array prototype like :
Array.prototype.myOwnFunction = function() {
for (var i = 0; i < this.length; i++) {
this[i] += 1;
}
return this;
};
const array = [1, 2, 3];
const result = array.myOwnFunction();
console.log(result);
Check the polyfill for Array.prototype.map(), this line in particular:
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
var O = Object(this);
Simplifying, this is where the values are received.
The way this works is related to the prototype and to the "thisBinding".
When a function is instantiated using new, the properties of the prototype are attached to the new Function object. As well, an "execution context" is created to maintain the environment. The properties of the prototype are then accessible in the environment through the current instance's this binding using this.
So if you have a function you want to use the data from in an instance, you can use a prototype as well. For many reasons (memory use, readability, reusability, inheritance, polymorphism, etc.) this is the best way to go about doing it. Modifying standard implementations such as Array, Math, Date, etc. are generally discouraged (although there will always be exceptions).
function LineItem(description,price,quantity){
this.description = description;
this.price = price;
this.quantity = quantity;
}
LineItem.prototype.Total = function(){
return this.price * this.quantity;
};
var avocados = new LineItem("avocado",2.99,3);
console.log(avocados.Total());
The main thing to take away here is that the thisBinding allows access to the current instance's object through this. That is where the data access comes from. For example, in an array instance, this references the current array; in a date instance, this references the current date; in the above LineItem example, this references the current LineItem.
Thanks to the responses to this question I was able to write a higher order component that accepts a callback function as an argument. Here is the code as an example:
Array.prototype.myFunction = function (callback) {
const items = this
const newArray = []
for (let item of items) {
item = callback(item)
newArray.push(item)
}
return newArray
}
const array = [1, 2, 3, 4]
const result = array.myFunction((item => {
return item + 1
}))
console.log(result)
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));