suppose we have this code
function largestOfFour(arr) {
return arr.map(Function.apply.bind(Math.max, null));
}
where arr is an array of arrays.
first,why must i use apply()?
I understand that when using the method Math.max() to operate on an array i must add the apply() method also. So i'll have something like this Math.max.apply(null, arr) why? what does apply() do?
In this code arr.map(Function.apply.bind(Math.max, null)) what does bind() really do?
Please give an explanation i can understand,i really appreciate this.
Looking at the entire expression:
arr.map(Function.apply.bind(Math.max, null));
map expects its first argument to be a function, which is returned by:
Function.apply.bind(Math.max, null);
Function.apply is a shorter version of Function.prototype.apply.
Calling bind on it returns a special version of apply whose this is set to Math.max and when called, will have as it's first parameter (i.e. the value that would normally be used as this) set to null, since it's not going to be used.
So each element in arr will effectively be called using:
Math.max.apply(null, member);
The use of apply means the values in member are passed as parameters, as if:
Math.max(member[0],member[1] ... member[n]);
So the expression returns the maximum value in each array. Those values are returned to map, which puts them into a new array.
var arr = [[1,2,3],[4,5,6]];
console.log(
arr.map(Function.apply.bind(Math.max, null)) //[3, 6]
);
and is effectively the same as:
var arr = [[1, 2, 3],[4, 5, 6]];
console.log(
arr.map(function(a) {return Math.max.apply(null, a)}) //[3, 6]
);
Though using recent features you might use destructing with rest parameter syntax:
var arr = [[1, 2, 3],[4, 5, 6]];
console.log(
arr.map(a => Math.max(...a)) // [3, 6]
);
Simply put, .apply calls a function with the set of arguments(array-like) passed to it.
EG:
const add = (...args) => args.reduce((acc, next) => acc + next);
I can call the add function with any number of arguments using the .apply method like this.
add.apply(null, [4, 2, 6, 76, 9]) // => 97.
You call also use .call but instead of passing in array-like arguments, you simply pass in the values
add.call(null, 4, 2, 6, 76, 9) // => 97.
With .bind, the difference is that it creates a new function with call be called later.
const addFunc = add.bind(null, 4, 2, 6, 76, 9);
addFunc() // -> 97.
So, as it applies to the function we defined, it also applies to inbuild functions like Math.max, Math.min, etc.
Hope this helps!
The Function.apply.bind(Math.max, null) creates a function definition when invoked takes null as the first parameter by default and any provided parameters will come second. So as a callback to arr.map this function (due to bind statement) will be bound to Math.max however the Function.apply's first parameter will be null and second is going the be the sub array item of the main array (of which the items are to be passed as arguments to Math.max function).
This is an old trick and in ES6 terms arr.map(s => Math.max(...s)); would do the same job much more clearly.
Related
I am trying to compare two given parameters of a function. The exact problem is as follows:
You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
Note
You have to use the arguments object.
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3)); // expected output: [1,1]
I am using filter method to iterate over the array but I couldn't compare the args with the elements of the array inside the callback of the filter.
function destroyer(arr, ...args) {
let result = arr.filter(num => {
for (let i = 0; i<=args.length; i++ ){
num !== args[i]
}
});
return result;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I can iterate with for loop but I cannot use the output of for loop to do filter iteration.
Any ideas?
Probably an easier way to achieve the goal using .filter() with .includes(). Additionally you can use ...rest so called rest parameters for you function, see form the documentation:
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
Try as the following:
const destroyer = (arr, ...rest) => {
return arr.filter(num => !rest.includes(num));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I hope this helps!
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
Example:
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
via MDN
Filter iterates over all elements of some array and returns a new array. It puts an element in the new array only if callback (your function invoked as a parameter of filter) return true otherwise it's omitted.
Next it's worth to use rest parameters to achieve two arrays (initial and values to exclude).
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
console.log(sum(1, 2, 3));
// expected output: 6
console.log(sum(1, 2, 3, 4));
// expected output: 10
Solution with explanation:
//Declare your function, first parameter is initial array, the second one is also array created by using rest parameters
function destroyer(initialArray = [], ...toExclude) {
// filter initialArray, if el (single element) is NOT included in "toExclude" it returns true
// and add this particular element to the result array
let result = initialArray.filter(el => toExclude.includes(el) == false);
//return result
return result;
}
I'm learning functional programming in JS and I'm doing it with Ramda.
I'm trying to make a function that takes parameters and returns a list. Here is the code:
const list = R.unapply(R.identity);
list(1, 2, 3); // => [1, 2, 3]
Now I tried doing this using pipe:
const otherList = R.pipe(R.identity, R.unapply);
otherList(1,2,3);
// => function(){return t(Array.prototype.slice.call(arguments,0))}
Which returns a weird function.
This:
const otherList = R.pipe(R.identity, R.unapply);
otherList(R.identity)(1,2,3); // => [1, 2, 3]
works for some reason.
I know this might be a newbie question, but how would you construct f(g(x)) with pipe, if f is unapply and g is identity?
Read the R.unapply docs. It's a function that gets a function and returns a function, which can take multiple parameters, collect it to a single array, and pass it as the parameter for the wrapped function.
So in the 1st case, it converts R.identity to a function that can receive multiple parameters and return an array.
In the 2nd case, R.unapply gets the result of R.identity - a single value, and not a function. If you pass R.identity as a parameter to the pipe, R.unapply gets a function and return a function, which is similar to the 1st case.
To make R.unapply work with R.pipe, you need to pass R.pipe to R.unapply:
const fn = R.unapply(R.pipe(
R.identity
))
const result = fn(1, 2, 3)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
It looks as though you really are thinking of pipe incorrectly.
When you use unapply(identity), you are passing the function identity to unapply.
But when you try pipe(identity, unapply), you get back a function that passes the results of calling identity to unapply.
That this works is mostly a coincidence: pipe(identity, unapply)(identity). Think of it as (...args) => unapply(identity(identity))(...args). Since identity(identity) is just identity, this turns into (...args) => unapply(identity)(...args), which can be simplified to unapply(identity). This only means something important because of the nature of identity.
You would use unapply to transform a function that would normally take its arguments as an array into a function that can take any number of positional arguments:
sum([1, 2, 3]); //=> 6
unapply(sum)(1, 2, 3) //=> 6
This allows you to, among many other things, map over any number of positional arguments:
unapply(map(inc))(1, 2) //=> [2, 3]
unapply(map(inc))(1, 2, 3) //=> [2, 3, 4]
unapply(map(inc))(1, 2, 3, 4) //=> [2, 3, 4, 5]
identity will always return its first argument. So unapply(identity)(1,2) is the same as identity([1,2]).
If your end goal was to create a function that returns a list of its arguments, I don't think you needed pipe in the first place. unapply(identity) was already doing that.
However, if what you need to do is to make sure that your pipe gets its parameters as a list, then you simply need to wrap pipe with unapply:
const sumplusplus = unapply(pipe(sum, inc, inc));
sumplusplus(1, 2, 3); //=> 8
I have been doing this course for hours on free code camp, however, I found a solution that I do not understand and I am trying to put comments on each line to record as I achieve and understand it for future references and I already understand some lines but I cannot understand some parts of this code:
function destroyer(arr) {
// let's make the arguments part of the array
var args = Array.prototype.slice.call(arguments); // this would result into [[1, 2, 3, 1, 2, 3], 2, 3]
args.splice(0,1); // now we remove the first argument index on the array so we have 2,3 in this example
// I DO NOT UNDERSTAND THESE CODES BELOW
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I already check on documentation and I find it hard to understand seems the code in this sample are very different. I would really appreciate your help!
arr in the section of code you don't understand refers to the first argument passed to the destroyer function; in this case, the array [1, 2, 3, 1, 2, 3]
arr.filter is using the Array.filter method to create a "filtered" version of the array with only those values that pass the "test" defined by function(element) { return args.indexOf(element) === -1; }
That function uses Array.indexOf to check if the sliced args array (which you correctly identified as being equal to [2, 3]) contains the given element. Because indexOf returns -1 when the element is not found, checking for that value is equivalent to checking that the specified element is NOT in the array
The result of all of this - and the return value of the function destroy - will be the array [1, 1], representing a filtered version of the array passed to destroy that contains all the values not equal to the other values passed to destroy.
Array.slice is part of the arrays prototype;
prototype methods are only accessable on instances of Classes.
var arr = ['a', 'b', 'c', 'd'];
// [] is JavaScript shorthand for instantiating an Array object.
// this means that you can call:
arr.slice(someArg1);
arry.splice(someArg2);
My question is about the map method of arrays in JavaScript.
You can pass it a function that takes a second argument, the index of the current element of the array being processed, but... to what purpose? What happens when you do it and what's the difference when you don't?
What would you use this feature for?
The index of the current item is always passed to the callback function, the only difference if you don't declare it in the function is that you can't access it by name.
Example:
[1,2,3].map(function(o, i){
console.log(i);
return 0;
});
[1,2,3].map(function(o){
console.log(arguments[1]); // it's still there
return 0;
});
Output:
0
1
2
0
1
2
Demo: http://jsfiddle.net/Guffa/k4x5vfzj/
Sometimes the index of the element matters. For example, this map replaces every second element with 0:
var a = [1, 2, 3, 4, 5, 6];
var b = a.map(function(el, index) {
return index % 2 ? 0 : el;
});
console.log(b);
Output:
[1, 0, 3, 0, 5, 0]
Here is a description of of the map function:
arr.map(callback[, thisArg])
callback
Function that produces an element of the new Array, taking three arguments:
currentValue
The current element being processed in the array.
index
The index of the current element being processed in the array.
array
The array map was called upon.
The map function takes a callback function as argument (and not an index as argument, as originally stated in the question before it was edited). The callback function has an index as a parameter - the callback is called automatically, so you don't provide the index yourself. If you only need the current value, you can omit the other parameters.
var t = [-12, 57, 22, 12, -120, -3];
t.map(Math.abs).reduce(function(current, previousResult) {
return Math.min(current, previousResult);
}); // returns 3
t.map(Math.abs).reduce(Math.min); // returns NaN
I don't understand why the second form doesn't work. Any explanations are welcomed.
EDIT: Technical context: Chrome and Firefox JavaScript engine. See ES5 reduce http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.21
Math.min accepts multiple arguments. This is exactly the same reason this doesn't work for parseInt or other functions like that. you need to bind the parameters yourself.
reduce feeds the values like index and array to Math.min
We can confirm this if we follow the following steps:
First, we proxy Math.min:
var oldMath = Math.min;
Math.min = function (){
console.log(arguments)
return oldMath.apply(Math, arguments);
}
Then we run the second version:
[-12, 57, 22, 12, -120, -3].reduce(Math.min);
Which logs:
[-12, 57, 1, Array[6]]
Since Array[6] is not a number, the result is NaN
Here is a very similar example from MDN:
["1", "2", "3"].map(parseInt);
While one could expect [1, 2, 3]
The actual result is [1, NaN, NaN]
parseInt is often used with one argument, but takes two. The second being the radix
To the callback function, Array.prototype.map passes 3 arguments: the element, the index, the array
The third argument is ignored by parseInt, but not the second one, hence the possible confusion.
reduce's callback is called passing four arguments: previousValue, currentValue, index and array. And because Math.min is a variadic function, your code:
t.map(Math.abs).reduce(Math.min); // returns NaN
is equivalent to:
t.map(Math.abs).reduce(function(current, previousResult, index, array) {
return Math.min(current, previousResult, index, array);
});
That's why the result is NaN: the last parameter, array, is not a number.
You can also solve this kind of issue with a high-ordered function like this one:
function binary (fn) {
return function (a, b) {
return fn.call(this, a, b);
}
}
And then:
t.map(Math.abs).reduce(binary(Math.min));
will works.