How is the 'apply ' method executed under the hood? - javascript

I have not understood the apply method in Javascript. From what I understood was the first parameter in the apply will be the desired value for the key word this, and the extra parameter would apply to the function the apply borrowed. I am still not sure how this apply method under the hood. How can the result be [1, 2, 3, 4, 5, 6, 7, 8]
var flattenedArray = [[1,2],[3,4,5,6], 7, 8]
[].concat.apply([],flattenedArray)
Could anyone help me how it actually works on this scenerio, please. Thanks

The result [1, 2, 3, 4, 5, 6, 7, 8] is not the value of Function#apply, but the function Array#concat.
apply only calls the function on the explicitly bound context and optionally takes parameters as array, which will be passed to the function.
When you pass [[1,2],[3,4,5,6], 7, 8] to the function apply it gets the inner items (the outer which keeps the inner items is considered to be the type of the 2nd parameter of apply, which is an array, so why it gets the inner items one by one and omit the outer array) which are [1,2] , [3,4,5,6], 7 and 8 to the function concat.
See the example. The below two calls of concat are identical.
const flattenedArray = [[1,2],[3,4,5,6], 7, 8];
const first = [].concat.apply([],flattenedArray);
console.log(first);
const second = [].concat([1,2], [3,4,5,6], 7, 8);
console.log(second);

var flattenedArray = [[1,2],[3,4,5,6], 7, 8]
[].concat.apply([],flattenedArray)
// [].concat.apply([],flattenedArray)
var concat = [].concat // ^^^^^^^^^
var me = [] // ^^
// [[1,2],[3,4,5,6], 7, 8]
var arg0 = [1,2] // ^^^^^
var arg1 = [3,4,5,6] // ^^^^^^^^^
var arg2 = 7 // ^
var arg3 = 8 // ^
me.concat = concat
var res = me.concat(arg0, arg1, arg2, arg3)
delete me.concat
console.log(res)
function concat(...args) {
var res = []
for (var x of this) {
res.push(x)
}
for (var arg of args) {
if (arg instanceof Array) {
for (var x of arg) {
res.push(x)
}
} else {
res.push(arg)
}
}
return res
}
console.log(concat.apply([], [[1,2], [3,4,5,6], 7, 8]))

Related

Pass an array and further arguments into a function. How?

I have a function which takes an array and further arguments like this:
function arrayandmore([1, 2, 3], 2, 3)
I need to return the array ([1, 2, 3]) without those elements which equals the arguments coming behind the array. So in this case, the returned array would be:
([1]).
One of my approaches is:
function destroyer(arr) {
var args = Array.from(arguments);
var i = 0;
while (i < args.length) {
var result = args[0].filter(word => word !== args[i]);
i++;
}
console.log(result);
}
destroyer([1, 1, 3, 4], 1, 3);
Console returns:
[ 1, 1, 4 ]
I don't understand, why it returns one too - I don't understand, why it does not work.
It is the same with using splice.
function destroyer(arr) {
var args = Array.from(arguments);
var quant = args.length - 1;
for (var i = 1; i <= quant; i++) {
if (arr.indexOf(args[i]) !== -1) {
arr = arr.splice(arr.indexOf(args[i]));
}
console.log(arr);
}
}
destroyer([1, 1, 3, 4], 1, 3);
I think, both ways should work. But I don't figure out why they don't.
Your while won't work because result is being overwritten in every loop. So, it only ever removes the last parameter to the destroyer function
You can use the rest parameter syntax to separate the array and the items to be removed.
Then use filter and includes like this:
function destroyer(arr, ...toRemove) {
return arr.filter(a => !toRemove.includes(a))
}
console.log(destroyer([1, 1, 3, 4, 5], 1, 3))

JavaScript: Calling Array.reduce when outer function is called, and outer function has arguments [duplicate]

This question already has answers here:
Is JavaScript a pass-by-reference or pass-by-value language?
(33 answers)
Does JavaScript pass by reference? [duplicate]
(13 answers)
Pass variables by reference in JavaScript
(16 answers)
Closed 4 years ago.
var foo = 0;
var bar = 0;
var arr1 = [1, 2, 3, 4, 5];
var arr2 = [6, 7, 8, 9, 10];
function getSum(arr, sum) {
sum = arr.reduce(function(accum, val) {
return accum += val;
}, 0)
}
getSum(arr1, foo); //expect foo to equal 15, but it stays at 0
getSum(arr2, bar); //expect bar to equal 40, but it stays at 0
I'd like to use this function multiple times, hence the arguments. I know having the sum argument equal Array.reduce() isn't what you're supposed to do, but I'm really not sure what the syntax should be.
Basically I want arr1 values to always be added up for foo, and arr2 always added up for bar, without having to repeat my code.
If I understand you correctly you just need a sum function like this:
var foo = 0;
var bar = 0;
var arr1 = [1, 2, 3, 4, 5];
var arr2 = [6, 7, 8, 9, 10];
const getSum = (arr) => arr.reduce((r,c) => r+c)
foo = getSum(arr1)
bar = getSum(arr2)
console.log(foo)
console.log(bar)
In JavaScript, primitive types are passed by value. Objects are passed by reference. You can accomplish this by wrapping foo up in an object, but you really should not design code this way. Code that mutates function arguments is often very hard to reason about (and debug).
var foo = { value: 0 };
var bar = { value: 0 };
var arr1 = [1, 2, 3, 4, 5];
var arr2 = [6, 7, 8, 9, 10];
function getSum(arr, sum) {
sum.value = arr.reduce(function(accum, val) {
return accum += val;
}, 0)
}
getSum(arr1, foo);
getSum(arr2, bar);
console.log(foo.value);
console.log(bar.value);
If possible, you should try to find a way to solve your problem without mutation.
In fact, many linting tools (like eslint) will throw an error if they see function arguments being mutated.
Instead, you should separate the sum function from the assignment of the resulting value:
var arr1 = [1, 2, 3, 4, 5];
var arr2 = [6, 7, 8, 9, 10];
function sum(arr) {
return arr.reduce(function(acc, next) { return acc + next });
}
var foo = sum(arr1);
var bar = sum(arr2);
console.log(foo);
console.log(bar);

Javascript have a function return no value

I've been trying to make a function that will not return any value if two values are the same between two arrays. I've searched my question several ways on here and on google, and everything I find says that a function HAS to return a value, and there's no way to avoid doing it. So I'm directly asking if there is a way, and if not how can I get around what I'm trying to do?
var bannedValues = [2, 4, 6, 8]; //Even numbers 1-8
var inputValues = [1, 2, 3, 4, 5, 6, 7, 8]; //All numbers 1-8
var filterValues = function(value)
{
for (var number of bannedValues)
{
if (value == number)
{
return; //I want this to return absolutely nothing, not even an empty value
}
}
return value;
}
var filteredValues = inputValues.map(filterValues);
console.log(filteredValues);
/* The array just gains "undefined" values in the places I want there
to be no value, so the array is a length of 8 instead of the 4 I want.
Can I somehow take those out or can I just not have them there in the first place? */
If you are using map, you are actually iterating through your array and manipulate it if necessary, but this will not remove item from array. Instead of map try filter
var bannedValues = [2, 4, 6, 8]; //Even numbers 1-8
var inputValues = [1, 2, 3, 4, 5, 6, 7, 8]; //All numbers 1-8
var filterValues = function(value) {
return bannedValues.indexOf(value)===-1;
}
var filteredValues = inputValues.filter(filterValues);
console.log(filteredValues);
result will be
(4) [1, 3, 5, 7]
Use the Array.filter() method:
var bannedValues = [2, 4, 6, 8]; //Even numbers 1-8
var inputValues = [1, 2, 3, 4, 5, 6, 7, 8]; //All numbers 1-8
filteredValues = inputValues.filter( function( el ) {
return bannedValues.indexOf( el ) < 0;
} );
/*This is another way to do it, but not supported in IE*/
filteredValues = inputValues.filter( function( el ) {
return !bannedValues.includes( el );
} );
console.log(filteredValues);
Lodash has an utility function for this as well: see here
JavaScript functions always have a return value. There's nothing you can do about that.
It's impossible I think to tell the difference between a function that explicitly returns undefined and one that just returns with no value expression.
If you want to do something like .map() but have the option of skipping values, use .reduce() instead:
var filterValues = function(accumulator, value)
{
for (var number of bannedValues)
{
if (value == number)
{
return accumulator;
}
}
accumulator.push(value);
return accumulator;
}
var filteredValues = inputValues.reduce(filterValues, []);

Javascript arguments inside a .filter method

I'm working on a problem, where I am provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. I need to remove all elements from the initial array that are of the same value as these arguments.
The function only takes 1 argument (the array), and there can be any number of additional arguments, which I can select as "arguments[index]".
Here is my code up to now:
function destroyer(arr) {
newArr= [ ];
newArr = arr.filter(function(x){
return x !== arguments[2]; //argumets[2] is equal to 3
});
return newArr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
// => returns [1, 2, 3, 1, 2, 3];
BUT if i enter 3, instead of arguments[2], the function returns [1,2,1,2].
What is going on?
Additionally, how can I loop through the code to test all other arguments, if I can't have a function in a loop?
arguments refers to the arguments of the second function
you can do something like this
function destroyer(arr) {
newArr= [ ];
var args = arguments;
newArr = arr.filter(function(x){
return x !== args[2];
});
return newArr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
First, make a reference to the top destroyer argument. Then use that inside your filter function.
function destroyer(arr) {
newArr= [ ]; var args = arguments;
newArr = arr.filter(function(x){
return x !== args[2]; //destoryer's arguments[2] is equal to 3
});
return newArr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);

Use my function in itself and return in 'correct' order

I have this function
var foo = function(){
var args = Array.prototype.slice.call(arguments, 0);
for (var i = 0; i < args.length; i++) {
console.log(args[i]);
}
};
that's used like this
foo(1, 2, 3, 4, 5, 6, 7, 8);
It's outcome
But I want it's usage to work like this, and get the same outcome
foo(1, 2, foo(3, 4, 5), 6, 7, 8);
Yet the out come for this is
Thanks in advance
Mach
But I want it's usage to work like this, and get the same outcome
foo(1, 2, foo(3, 4, 5), 6, 7, 8);
You can't. That line is equivalent to this:
var tmp = foo(3, 4, 5);
foo(1, 2, tmp, 6, 7, 8);
That is, first the foo(3, 4, 5) bit runs, and then the foo(1, 2, mumble, 6, 7, 8) bit runs.
There are games you can play to defer execution, but they will tend to be very specific to what you're actually doing (which I assume isn't just outputting numbers in order).
For instance, a game you could play here would be to have foo detect whether its arguments are functions and, if so, call them; then you could use Function#bind (or similar) to create functions that, when called, will have the arguments you want. That looks like this:
var foo = function(){
var args = Array.prototype.slice.call(arguments, 0);
var arg;
for (var i = 0; i < args.length; i++) {
arg = args[i];
if (typeof arg === "function") {
arg();
} else {
console.log(arg);
}
}
};
and then used like this:
foo(1, 2, foo.bind(undefined, 3, 4, 5), 6, 7, 8);
Live Example (source)
That works because Function#bind doesn't call the function, it creates a new function that, when called, will get called with the arguments you gave bind (the first argument is what this will be during the call; if you don't have anything specific it needs to be, use undefined or null). So in effect when we do this:
foo(1, 2, foo.bind(undefined, 3, 4, 5), 6, 7, 8);
...we're doing this:
var tmpFunction = foo.bind(undefined, 3, 4, 5);
foo(1, 2, tmpFunction, 6, 7, 8);
(Function#bind is an ES5 feature present in all modern browsers except IE8 (which while not "modern" is still in significant use), but it can be correctly "shimmed" using es5-shim or similiar.)
(Trivia: This thing where you create a function with some of its arguments "baked into" it is called currying a function, so named after the mathematician Haskell Curry. [And yes, that's where the programming language Haskell gets its name.])
You're calling foo(3,4,5) and putting it's return value to the second call to foo(...). Your code can be written as:
var ret = foo(3,4,5);
foo(1, 2, ret, 6, 7, 8);
I am unaware what console.log accepts as arguments, but if it can handle a function, you could do:
var func = function() { foo(3, 4, 5); };
foo(1, 2, func, 6, 7, 8);
First you have to return the args in your foo() as so:
var foo = function(){
var args = Array.prototype.slice.call(arguments, 0);
for (var i = 0; i < args.length; i++) {
console.log(args[i]);
}
return args;
};
But, even so you would get an Array as the 3rd element of your array when you call:
foo(1, 2, foo(3, 4, 5), 6, 7, 8);
You will have to filter your arguments so if there's an array found among your arguments, you should split that into scalars and then pass them to the slice method.
Hope this helped, keep me posted.

Categories

Resources