How to use Math.max, etc. as higher-order functions - javascript

In short, this works:
[1, 2, 3].reduce(function (a, b) { return Math.max(a, b); });
=> 3
But this doesn't:
[1, 2, 3].reduce(Math.max);
=> NaN
Pure puzzlement.
This is in Firefox 3.5.9, which I presume is using the mozilla standard implementation of reduce, FWIW.

Math.max can be used as a higher-order function. The problem is .reduce will call the function with 4 arguments:
Math.max(accumulator, value, index, the_array)
here is the_array is an array, so Math.max returns NaN. I don't think there's simpler way to discard the last 2 arguments.

Math.max.apply(Math, [1, 2, 3]);
//3

Not exactly a higher order use, but this also works:
Math.max(...myArray);

Related

the DOM doesn't work with me when I try min and max and it show NaN [duplicate]

I am trying to get the highest number from a simple array:
data = [4, 2, 6, 1, 3, 7, 5, 3];
alert(Math.max(data));
I have read that if even one of the values in the array can't be converted to number, it will return NaN, but in my case, I have double-checked with typeof to make sure they are all numbers, so what can be my problem?
The reason why your code doesn't work is because Math.max is expecting each parameter to be a valid number. This is indicated in the documentation as follows:
If at least one of arguments cannot be converted to a number, the result is NaN.
In your instance you are only providing 1 argument, and that 1 value is an array not a number (it doesn't go as far as checking what is in an array, it just stops at knowing it isn't a valid number).
One possible solution is to explicitly call the function by passing an array of arguments. Like so:
Math.max.apply(Math, data);
What this effectively does is the same as if you manually specified each argument without an array:
Math.max(4, 2, 6, 1, 3, 7, 5, 3);
And as you can see, each argument is now a valid number, so it will work as expected.
Spreading an array
You can also spread the array. This essentially treats the array as if each item is being passed as it's own argument.
Math.max(...data);
if you see doc for Math.max you can see next description
Because max() is a static method of Math, you always use it as Math.max(), rather than as a method of a Math object you created (Math is not a constructor).
If no arguments are given, the result is -Infinity.
If at least one of arguments cannot be converted to a number, the result is NaN.
When you call Math.max with array parameter like
Math.max([1,2,3])
you call this function with one parameter - [1,2,3] and javascript try convert it to number and get ("1,2,3" -> NaN) fail.
So result as expected - NaN
NOTE: if array with just one number - all work correctly
Math.max([23]) // return 23
because [23] -> "23" -> 23 and covert to Number is done.
If you want get max element from array you should use apply function, like
Math.max.apply(Math,[1,2,3])
or you can use the new spread operator
Math.max(...[1,2,3])
It's not working because you are passing an array as the parameter instead of comma separated numbers. Try spreading the array like this:
data = [4, 2, 6, 1, 3, 7, 5, 3];
alert(Math.max(...data));
If you have to use the Math.max function and one number of the array might be undefined, you could use:
x = Math.max(undefined || 0, 5)
console.log(x) // prints 5
You will need to use the spread operator while passing array as an argument to the Math.max() method.
alert(Math.max(...data)); //this should resolve it.

JavaScript - Apply function call to object with another prototype

The task (actually the problem) I'm facing is the following. I have an array (let’s call it arr). The first element of the array is a function and the rest are arguments. For example:
arr = [(a, b) => a + b, 5, 7];
So, I need to call function (a, b) => a + b with arguments 5 and 7. However, I don't want to use arr[0](...arr.slice(1)), instead I want to do it using Function.prototype.apply and Function.prototype.call (I can also use Function.prototype.bind if needed). So basically, I am allowed to access arr only once and no other variables may be used. Also, I'm not allowed to modify arr or it's properties, or to store any other data somewhere (like as property of some global object, etc). Is that even possible?
My attempt
I tried to figure it out, and this is what I came up with (it doesn't work):
Function.prototype.apply.call(this, ...arr);
However, it throws an error saying:
Uncaught TypeError: Function.prototype.apply was called on #Global, which is an object and not a function
Question
What is the correct way to do this?
instead I want to do it using Function.prototype.apply and Function.prototype.call. Is that even possible?
Sure, but we have to repeat arr:
arr[0].call(...arr)
Live Example:
const arr = [(a, b) => a + b, 5, 7];
console.log(arr[0].call(...arr));
That wouldn't work if the function required a specific this, but your function doesn't, for two reasons, either of which would be sufficient: 1. It's an arrow function, so it doesn't care what this we call it with, it closes over the one where it was created; and 2. It doesn't use this anyway.
That said, probably better just to give yourself a helper function that does it instead.
You can use destructuring to get the elements of arr array as variables, then call the function at index 0 with elements at indexes 1-N
let arr = [(a, b) => a + b, 5, 7];
let [fn, a, b] = arr;
console.log(fn(a, b));
For any number of elements at indexes 1-N you can use rest element
let arr = [(a, b) => a + b, 5, 7];
let [fn, ...rest] = arr;
console.log(fn.apply(null, rest));
Alternatively, using Function, template literal to convert array to string, .replace() with RegExp /\s/g with .replace() to remove space characters, .match() with RegExp /(\(.*\)=>[^,]+(?=,))|[^,]{1}[^\1]+/g to capture arrow function, negate first comma of characters that are not arrow function and arrow function captured at first group, we can reference arr once and not create additional variables save for immediately invoked arrow function parameters
let arr = [(a, b) => a + b, 5, 7];
console.log(
(([a, b]) => new Function(`return (${a})(${b.split(/,/)})`)())
(`${arr}`.replace(/\s/g, "").match(/(\(.*\)=>[^,]+(?=,))|[^,]{1}[^\1]+/g))
)

Why can't we feed Array.prototype.concat directly into reduce?

JavaScript newbie here. Today I learned about reduce and set out to implement my own array flatten function.
What I had was
var array = [[1, 2], [3, 4, 5], [6]];
var result = array.reduce(Array.prototype.concat, []); // Causes Uncaught TypeError: Array.prototype.concat called on null or undefined
var result = array.reduce(Array.prototype.concat.call, []); // Causes Uncaught TypeError: undefined is not a function
While answers in Merge/flatten an array of arrays in JavaScript? are elegant and idiomatic, I'd really appreciate an illustration on how my attempts failed.
your code is equivalent of
result = array.reduce(fn, []);
function fn(a, b, index, array) { // this is the arguments reduce sends to the callback
return Array.prototype.concat.call(null, a, b, index, array);
}
can you spot the problem?
You have the right idea with Array.prototype.concat.call. With Array.prototype.concat, the calls are going to look like this:
var concat = Array.prototype.concat;
concat(concat(concat([],
[1, 2]),
[3, 4, 5]),
[6])
which doesn’t work because Array.prototype.concat concatenates its arguments to its this; calling it as concat() gives it a this of undefined. How about with call?
var call = Array.prototype.concat.call;
call(call(call([],
[1, 2]),
[3, 4, 5]),
[6])
This runs into the same problem, but with Function.prototype.call (Array.prototype.concat is a function like any other, and inherits its call method from Function.prototype). call tries to call its this, but calling it as call() gives it a this of undefined.
You could pass Function.prototype.call.bind(Array.prototype.concat)… if reduce didn’t call its function with more arguments than just the accumulator and the current item. But it does, passing the index of the current item and context array as well, and ruining any chance of making this work by passing only Array.prototype.concat wrapped with some builtins.
This is so because Array.prototype.concat requires a function prototype such that it is an array or list of values to be concatenated to the main array.
However, the callback to Array.prototype.reduce requires an accumulator, currentValue, currentIndex and array as parameters.
The parameters (as well as what they're supposed to do) don't match and hence, you get unexpected results or errors.

passing anonymous functions as parameters in livescript

What's the correct way to pass a function as a parameter in liveScript?
For instance, lets say I Want to use the array reduce function, in convectional javascript I would write it as the following
myArray.reduce(function (a,b) {return a + b});
This translates to liveScript quite nicely as:
myArray.reduce (a,b) -> a + b
Now, I want to set the initial value by providing a second parameter:
myArray.reduce(function (a,b) {return a + b},5);
How would I translate this to liveScript? It seems that the first function overrides any ability to pass additional parameters to reduce.
Apologies if I have missed something obvious, but I can't seem to find anything pertaining to this scenario in the docs
For more complex functions I'd recommend you to use this style
[1, 2, 3].reduce do
(a, b) ->
# your code here
0
You can use ~ to bind the this argument, then call flip on it to swap the first and second parameters:
flip [1, 2, 3]~reduce, 0, (a, b) -> a + b
This may be more readable if the callback body is very long.
You have to wrap the closure in ()
[1,2,3].reduce ((a,b) -> a + b), 0
Compiles to
[1, 2, 3].reduce(function(a, b){
return a + b;
}, 0);
Just to complement the other answer, LiveScript offers binops, just put parentheses around the operator.
[1 2 3].reduce (+), 0

Why is math.max() returning NaN on an array of integers?

I am trying to get the highest number from a simple array:
data = [4, 2, 6, 1, 3, 7, 5, 3];
alert(Math.max(data));
I have read that if even one of the values in the array can't be converted to number, it will return NaN, but in my case, I have double-checked with typeof to make sure they are all numbers, so what can be my problem?
The reason why your code doesn't work is because Math.max is expecting each parameter to be a valid number. This is indicated in the documentation as follows:
If at least one of arguments cannot be converted to a number, the result is NaN.
In your instance you are only providing 1 argument, and that 1 value is an array not a number (it doesn't go as far as checking what is in an array, it just stops at knowing it isn't a valid number).
One possible solution is to explicitly call the function by passing an array of arguments. Like so:
Math.max.apply(Math, data);
What this effectively does is the same as if you manually specified each argument without an array:
Math.max(4, 2, 6, 1, 3, 7, 5, 3);
And as you can see, each argument is now a valid number, so it will work as expected.
Spreading an array
You can also spread the array. This essentially treats the array as if each item is being passed as it's own argument.
Math.max(...data);
if you see doc for Math.max you can see next description
Because max() is a static method of Math, you always use it as Math.max(), rather than as a method of a Math object you created (Math is not a constructor).
If no arguments are given, the result is -Infinity.
If at least one of arguments cannot be converted to a number, the result is NaN.
When you call Math.max with array parameter like
Math.max([1,2,3])
you call this function with one parameter - [1,2,3] and javascript try convert it to number and get ("1,2,3" -> NaN) fail.
So result as expected - NaN
NOTE: if array with just one number - all work correctly
Math.max([23]) // return 23
because [23] -> "23" -> 23 and covert to Number is done.
If you want get max element from array you should use apply function, like
Math.max.apply(Math,[1,2,3])
or you can use the new spread operator
Math.max(...[1,2,3])
It's not working because you are passing an array as the parameter instead of comma separated numbers. Try spreading the array like this:
data = [4, 2, 6, 1, 3, 7, 5, 3];
alert(Math.max(...data));
If you have to use the Math.max function and one number of the array might be undefined, you could use:
x = Math.max(undefined || 0, 5)
console.log(x) // prints 5
You will need to use the spread operator while passing array as an argument to the Math.max() method.
alert(Math.max(...data)); //this should resolve it.

Categories

Resources