JavaScript Array#map: index argument - javascript

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.

Related

Why does the pop() method stop working when used within a map method?

I want to reverse an array in place without using reverse method. I tried the following code:
function reverseArray(arr) {
return arr.map(() => {
return arr.pop()
})
}
console.log(reverseArray([1, 2, 3, 4]));
//logs [4,3,undefined,undefined]
Using dev tools debugger, this is what logs in the console: (4) [4, 3, empty × 2].
arr.pop() returns undefined if called on an empty array but it shouldn't be empty in this case, to my understanding.
Question: what's happening? Why are the first two elements printing correctly and the rest aren't?
You're removing elements from the array while you're iterating over it. The documentation says:
The range of elements processed by map is set before the first invocation of callback. ... Elements that are deleted after the call to map begins and before being visited are not visited.
So map() determines at the beginning that it will iterate 4 times, creating an array with 4 elements.
On the first iteration, arr.pop() removes arr[3]. On the second iteration it removes arr[2].
The third iteration expects to process arr[2]. But since that element doesn't exist any more, it doesn't call the callback function, and just stores undefined in the result array. The same thing happens on the fourth iteration.
You could iterate over a copy of the array.
function reverseArray(arr) {
return [...arr].map(() => {
return arr.pop()
})
}
console.log(reverseArray([1, 2, 3, 4]));
The problem is, that you pop some elements and therefore, they don't exist in the array anymore. So, the first two elements are poped and two other ones are written in those spots, but internally they don't exist anymore in the mean time.
There are also many other (better) solutions like this one (from here):
function reverse (array) {
var i = 0,
n = array.length,
middle = Math.floor(n / 2),
temp = null;
for (; i < middle; i += 1) {
temp = array[i];
array[i] = array[n - 1 - i];
array[n - 1 - i] = temp;
}
}

Iterating inside JavaScript filter method

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;
}

Someone please explain the Function.apply.bind(Math.max, null) algorithm

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.

How to pass a second parameter to Javascript map [duplicate]

This question already has answers here:
Using javascript map with a function that has two arguments
(8 answers)
Closed 6 years ago.
I need to pass an array index as a second parameter to Javascript .map() :
var arr1 = [25,50,75,90]
var arr2 = [1,2,3,4,5]
arr2.map(function(x){
console.log( arr1[index] * x );
});
How can I iterate on arr1 while applying .map() to arr2?
Thanks!
You don't pass a second parameter to map, you pass a second parameter to the function that you pass as a first (and only) parameter to map. When map, in turn invokes that function, it will call it with the current element as the first parameter, and the index of the current element as the second. (Map actually uses a third argument as well, which is the whole array being iterated).
So if you have code like:
arr2.map(function(item, index){
console.log( item, index );
});
You'll get
25 0
50 1
75 2
90 3
if what you need is an array like this:
var arr3 = [25 * 1, 50 * 2, 75 * 3, 90 * 4, 100 * 5];
you may use something like:
var arr1 = [25,50,75,90,100];
var arr2 = [1,2,3,4,5];
var arr3 = arr2.map(function(x, idx){ return x * arr1[idx]; });
Note both arrays should have the same length.
As stated in the Mozilla documentation
The map() function provides a callback which takes three arguments: currentValue, index, array

Empty elements in JS array declaration

it's my first question here, after have been reading for years, so be nice with me please.
I'm having trouble with array management in js/jq.
I have array with several elements, which is processed with $.each function.
I want to extract matching elements to another array and return this array.
But for some reason (don't know if it's because array declaration, jquery.each function...) I'm having first empty element.
I think I'm making this more difficult to understand than it's, so made jsfiddle.
var arr = new Array();
$.each([1,2,3], function(index,element){
if (element == 2){
arr[index] = element;
}
});
arr must have only 1 element, but arr.length returns 2 because first array slot is empty.
Here is a fiddle http://jsfiddle.net/moay7y95/
I'm so sure that it's a simple and little stupid thing, but I've not been able to find an answer.
Thanks in advance!
You are pushing an element in array at 1st index. So, javascript by default put undefined at 0th index.
So, at the end of each your array will be
[undefined, 2];
This is the reason you get the length as 2.
You can use push to add element in array:
$.each([1, 2, 3], function (index, element) {
if (element == 2) {
arr.push(element);
elem++;
}
});
Demo: https://jsfiddle.net/tusharj/moay7y95/2/
The push() method adds one or more elements to the end of an array and returns the new length of the array.
Docs: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/push
Best solution: Use .filter
var arr = [1, 2, 3].filter(function(value){
return value == 2
});
If the return value is true, the element is included in the returned array, otherwise it is ignored. This is pure js, so you don't need jquery. See documentation about .filter
Your problem: Use .push
Try using the .push method on the array (see Mozilla's documentation).
var arr = new Array();
$.each([1,2,3], function(index,element){
if (element == 2){
arr.push(element);
}
});
The .push method will dynamically grow your array as elements are added.
Your problem is that the value of index inside your function is a reference point in your initial array. This means that if you look at the returned array, you will find [undefined, 2] and so the length is returning as 2. If your condition were element == 3 you would have two empty slots.
Alternatively: Use .grep
Another jQuery method is $.grep. See http://api.jquery.com/jquery.grep/
var arr = $.grep([1, 2, 3], function (value) {
return value == 2
});
This is jQuery's implementation of javascript's .filter.
Your array [1, 2, 3] has three elements, and three associated indexes: 0, 1 and 2. In each iteration of your $.each() loop over that array, the index and its value get passed to the function.
So the first call gets 0, 1 passed as its arguments. The second call gets 1, 2 passed as its arguments, then the if statement condition evaluates to true, so your code:
arr[index] = element;
is actually equivalent to
arr[1] = 2;
Since you're inserting an element at index 1, you'll end up with an empty index 0.
You could instead simply use Array.push() to add the element to the array:
arr.push(element);
As already mentioned the problem is you are assigning the value at index 1, so the array will be considered of length 2.
One solution is to use .push() instead of assigning based on index.
Another approach could be is to use Array.filter() and return true if the element matches the conditon
var arr = [1, 2, 3].filter(function(value){
return value == 2
});
Demo: Fiddle
Using $.grep()
var arr = $.grep([1, 2, 3], function (value) {
return value == 2
});
[1, 2, 3]
if (element == 2)
arr[index] = element;
This means you're setting arr[1] to element.
If you want to add it to the arr sequentially (filling it up normally), push it into art
arr.push(element);
Seems like you're looking for Array.filter (MDN).
var elems = ["car", "bike"];
var arr = [1, 2, 3, "car", 5, "bike"].filter(myFilterFunc);
function myFilterFunc(val) {
if (elems.indexOf(val) > -1) return true;
}
Fiddle

Categories

Resources