What is .apply doing in this JavaScript code? - javascript

I am doing the Netflix tutorial on Reactive Programming and I came across this piece of code that I don't fully understand.
Array.prototype.mergeAll = function() {
var results = [];
this.forEach(function(subArray) {
results.push.apply(results, subArray);
});
return results;
};
Why use apply in this line of code results.push.apply(results, subArray);? Why not just use results.push(subArray)? What is the difference in the code?

The results wouldn't be the same at all. You can test this for yourself:
results = [1, 2, 3];
subArray = [4, 5, 6];
results.push(subArray);
console.log("With push:", results); // With push: [1, 2, 3, [4, 5, 6]]
results = [1, 2, 3]; // reset results
results.push.apply(results, subArray);
console.log("With apply:", results); // With apply: [1, 2, 3, 4, 5, 6]
apply accepts an array of arguments, and the elements of the array become individual arguments to the function.
This lets you invoke the function push with an arbitrary number of arguments, where each argument is added to the array. Simply calling results.push(...) would invoke the function with a single argument, which would be an array, resulting in the entire subArray being pushed onto results as one element.
In the above example, results.push.apply(results, subArray) is equivalent to calling results.push(4, 5, 6), while results.push(subArray) simply invokes results.push([4, 5, 6]).
The net effect of using one over the other is that, given an input array containing sub-arrays...
[[1, 2, 3], [4, 5, 6], [7, 8], [9]]
... using results.push would produce an identical array, where each sub-array was pushed onto results as a single element:
[[1, 2, 3], [4, 5, 6], [7, 8], [9]]
Using results.push.apply would cause each element from each sub-array to be pushed onto results as its own element, resulting in the desired flattened array:
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Example 1:
var results = [];
results.push.apply(results,[2,3,4])
results [2,3,4]
Example 2:
var results2 = [];
[].push.apply(results2,[2,3,4])
// results2 [2,3,4]
results.push.apply is identical to [].push.apply. Both of them represent array's push method.
the first parameter in apply() method : results / results2, representing the scope/this inside Array.prototype.push method.
Here is the link giving a good example, how the first parameter in apply() method works.
If you want to know, why the second parameter of apply() method is array of arguments. This link's example has good explanation.
Basically
[].push.apply(results2,[2,3,4])
// results2 [2, 3, 4]
result2.push(2,3,4)
// results2 [2, 3, 4]
The first method is equal to the second method

Related

How to verify JavaScript memory consumption by a piece of code

both javascript code accomplish the same task merge the second array into the first one, i have read various blogs etc claming Option 2 is more memory efficient,
Is there a way or a tool to verify and see it for myself the memory usage in the Option 2 is lower ?
Option 1
//consume a lot of memory
var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
console.log(array1.concat(array2));
Option 2
//reduce the memory usage
var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
console.log(array1.push.apply(array1, array2));
i tried this approach https://www.valentinog.com/blog/node-usage/ node js code, not much helpful
also tried https://github.com/paulirish/memory-stats.js dosent seems to work
Your premise is wrong. Both scripts do not accomplish the same task.
Your first option allocates a new result array with 6 elements, and leaves both array1 and array2 untouched. (See the documentation for Array.prototype.concat().)
var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
console.log(array1.concat(array2)); // [1, 2, 3, 4, 5, 6]
console.log(array1); // [1, 2, 3]
console.log(array2); // [4, 5, 6]
Your second option merges the contents of array2 into array1. (See the documentation for Array.prototype.push().)
var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
console.log(array1.push.apply(array1, array2)); // 6
console.log(array1); // [1, 2, 3, 4, 5, 6]
console.log(array2); // [4, 5, 6]
So the second option only requires a total of 9 array elements, while the first option requires 12. On a language level, option 2 is 25% more memory-efficient. How much memory is actually used by the JavaScript engine you're using is an implementation detail. In addition, contrived examples like these are typically not good candidates for reliable performance testing as the engine's JavaScript interpreter might apply various optimizations.

Why do some operations not alter an array that was passed to a function?

Scenario 1:
var myArray = [2, 3, 4, 5];
function doStuff(arr) {
arr = [];
}
doStuff(myArray);
console.log(myArray); // [2,3,4,5]
Scenario 2:
var myArray = [2, 3, 4, 5];
function doStuff(arr) {
arr.pop();
}
doStuff(myArray);
console.log(myArray); // [2,3,4]
Why does scenario 1 not update the globally declared array but scenario 2 does?
In the first example:
You are altering the variable arr which is merely just holding a reference to the array [2, 3, 4, 5], so instead of holding a reference to [2, 3, 4, 5], it will hold a reference to another array [].
At the line var myArray = [2, 3, 4, 5];:
myArray -----------------------------------> [2, 3, 4, 5]
Then at the line doStuff(myArray);:
myArray -----------------------------------> [2, 3, 4, 5]
↑
arr ----------------------------------------------/
Then at the line arr = [];:
myArray -----------------------------------> [2, 3, 4, 5]
arr ---------------------------------------> []
=> So, after the call to doStuff, myArray is still [2, 3, 4, 5].
In the second example:
You are using the reference to [2, 3, 4, 5] stored in arr to call a function pop on it that alters it.
At the line var myArray = [2, 3, 4, 5];:
myArray -----------------------------------> [2, 3, 4, 5]
Then at the line doStuff(myArray);:
myArray -----------------------------------> [2, 3, 4, 5]
↑
arr ----------------------------------------------/
Then at the line arr.pop();:
myArray -----------------------------------> [2, 3, 4, 5].pop()
↑
arr.pop() ----------------------------------------/
Which alters the array to:
myArray -----------------------------------> [2, 3, 4]
↑
arr ----------------------------------------------/
=> So, after the call to doStuff, myArray is now [2, 3, 4].
In the first case you are doing a new memory allocation while you are actually modifying the array in the second case.
Hence in the first case the value does not get modified.
For eg:
var myArrayOne = [2, 3, 4, 5];
function doStuff(arr) {
arr = [7,8,9]; //assigining a whole new object to the copy
}
doStuff(myArrayOne);
console.log(myArrayOne); // [2,3,4,5]
// Scenario 2
var myArrayTwo = [2, 3, 4, 5];
function doStuff(arr) {
arr.pop(); //modifying the internals of the arr object
}
doStuff(myArrayTwo);
console.log(myArrayTwo); // [2,3,4]
arr is a reference to the array, which exists somewhere in memory (you don't know where, and you don't care). When you say arr = [], you're creating a new array somewhere in memory, and changing arr to refer to that new array. The old array still exists in memory. If there was nothing referring to the old array then it would eventually be garbage collected, but in this case it's still referred to by myArray so it remains untouched.
arr.pop() on the other hand is modifying the array, not changing references.
In the first function, you're just reassigning the parameter of the function. That has no effect on the passed data.
In the second, you're actually mutating the passed array via the call to pop.
Think of it this way: in
var a = 1
a = 2
Is the 1 modified in any way? No, the reference a that was pointing to it was just changed.

Merging the elements of nested arrays into one big array

I'm trying to merge the elements of the array into one big array. But I receive a message saying:
ReferenceError: reduce is not defined
Here is my code:
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(reduce(arrays, function(arrayOne, arrayTwo){
return arrayOne.concat(arrayTwo);
}, 0));
reduce() is a method of the Array object, so you must use arrays.reduce().
Moreover, since your initial value is set to 0 (the 2nd parameter), you can't use arrayOne.concat on it, since it's not an array, so you must set the initial value to [].
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(arrayOne, arrayTwo){
return arrayOne.concat(arrayTwo);
}, []));
Note that calling Array.flat is easier:
var arrays = [[1, 2, 3], [4, 5], [6]];
// If you expect a multi-level nested array, you should increase the depth.
var depth = 1;
console.log(arrays.flat(depth));
reduce() is only defined on Arrays, you cannot call it by itself:
arrays.reduce(
function (a, b) { return a.concat(b); }
);
// Array [ 1, 2, 3, 4, 5, 6 ]

Is array.slice enough to handle a multidimensional Array in JavaScript?

Is array.slice enough to clone a multidimensional Array in JavaScript?
For example:
var array = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
var b = array.slice();
console.log(b);
I saw a secondary implementation as this from Play by Play: Lea Verou on pluralsight:
b = array.slice().map( function(row){ return row.slice(); });
The docs are pretty clear:
The slice() method returns a shallow copy of a portion of an array into a new array object.
So the answer is no: slice by itself is not enough to clone a multidimensional array. It will only clone one dimension. You need to recursively clone each element (the way the "secondary implementation" you posted is doing) if you want a deep clone.
From MDN:
The slice() method returns a shallow copy of a portion of an array into a new array object.
That is, it will not clone the internal arrays / objects. Observe:
var array = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
var b = array.slice();
b[0].push(10);
console.log(array);
> [ [1, 2, 3, 10], [4, 5, 6], [7, 8, 9] ]
So if you want a deep copy of your 2D array, then you'd have to go with the map method (or something similar).

underscore.js - Is there a function that produces an array thats the difference of two arrays?

Looking for a function in underscore.js that will take 2 arrays and return a new array of unique values? Something like _without
_.without([0, 1, 3, 9], [1, 3]);
I would like => [0,9] returned
It appears _without's 2nd arg is a list of values, not an array. Anyone out there know
if underscore has the specific function I'm looking for? Or can I take an exisitng array and covert it to values the function expects.
Thanks,
~ck in San Diego
The _.difference function should give you what you're looking for:
_.difference([0, 1, 3, 9], [1, 3]); // => [0, 9]
_.without.apply(_, [arr1].concat(arr2))
[[0, 1, 3, 9]].concat([1, 3]) is [[0, 1, 3, 9], 1, 3];
_.without.apply(_, [[0, 1, 3, 9], 1, 3]) is _.without([0, 1, 3, 9], 1, 3)
You've got a perfectly good _.without method. So just convert an array into a list of values you can pass into a function. This is the purpose of Function.prototype.apply
var result = _.reject([0, 1, 3, 9], function(num) {
return _.include([1, 3], num);
});
Lo-Dash is extended Underscore and here is what you need: http://lodash.com/docs#xor
_.xor
Creates an array that is the symmetric difference of the provided arrays. See http://en.wikipedia.org/wiki/Symmetric_difference.
_.xor([1, 2, 3], [5, 2, 1, 4]);
// → [3, 5, 4]

Categories

Resources