What does 'this' keyword Refer to in this Method - javascript

I'm just a bit confused about how the this keyword is used in this context. It is placed in an anonymous function with the parameter callback and is then used as such: callback(this[i], i, this). The exercise doesn't go into depth, but I understand that the this is referring to the ar object that is in the __proto__. Why are 3 arguments being placed in the anonymous function's parameter callback(this[i],i,this)and how is it working under the hood? Any insight would be greatly appreciated, thank you.
Just to add to what was previously said, the exercise asked for me to implement my own version of Array.prototype.map.
Array.prototype.map = function(callback) {
let arr = [];
for(let i = 0; i < this.length; i++){
arr.push(callback(this[i], i , this))
}
return arr;
}
let ar = new Array()

The map function will be called on an Array instance, so in the following lines of code:
for(let i = 0; i < this.length; i++){
arr.push(callback(this[i], i , this))
}
is as per the signature of the callback function passed to the Array.prototype.map method:
callback Function that produces an element of the new Array, taking
three arguments:
currentValue The current element being processed in the array.
index Optional The index of the current element being processed in the
array.
array Optional The array map was called upon.
So to breakdown the the three arguments in the callback function in your snippet:
this[i] translates to the first parameter of the map callback, which is the currentValue. As you understood correctly this is the array instance on which the map is called. So this[i] will refer to the value in the ith index of the array
i is the index or current index. This is the ith index of the iteration of the for loop being sent to the callback.
this is an reference to the array itself on which the map is invoked.
const arr = [1, 2, 3];
const callback = function(currentElement, index, array){
console.log(`The current element ${currentElement}`);
console.log(`The current index ${index}`);
console.log(`The array instance ${array}`);
return currentElement * 2; //multiplies each element with 2, this is the mapping operation
}
const mappedArray = arr.map(callback);
console.log(`The mapped array ${mappedArray}`);

Related

Why Do I need to pass an Object to Array.from?

I don't understand why the followings provide different results, and on the second one I see people using an underscore (_, i), but I am not really sure what it does.
let test1 = Array.from(5, (v,i) => i); //empty array;
let test2 = Array.from({length:5}, (v,i) => i); // [0,1,2,3,4]
I don't get why I need to pass an object to get an array populated of the first n numbers.
Could someone please explain?
Let's see the signature of Array.from() method first.
Array.from(arrayLike, mapFn)
So, the first argument should be an arrayLike object (an object that has a length property of a non-negative integer). You can pass an array, string, etc. as well.
The second parameter is a map function which maps the the elements of the first argument (arrayLike) to the returned value from the callback function.
This map function takes a callback function with following signature.
callback(value, index)
The map function creates a new array populated with the results of calling the callback function (v, i) => i on every element in first argument of Array.from.
Now, let's see first case,
let test1 = Array.from(5, (v,i) => i);
Here, (v, i) => i is a map function. v is element of arrayLike object and i is the index of that element. The index i is returned.
But, 5 is neither an array nor an arrayLike object and the length property of 5 is undefined. So, map function maps no (zero) element and produces an empty array. Think of the following implementation.
function mapFn (arrayLike, callback) {
let result = [];
for (let i = 0; i < arrayLike.length; i++) {
let mappedValue = callback(arrayLike[i], i);
result.push(mappedValue);
}
return result;
}
If you pass 5 to the mapFn function, the for loop iterates 0 times and mapFn returns an empty array.
In case of second example,
let test2 = Array.from({length:5}, (v,i) => i); // [0,1,2,3,4]
{length: 5} is an arrayLike object. The map function produces an array with returned values of the callback function. The callback function returns index. Hence, [0,1,2,3,4].
In the callback function only the i parameter is used and returned from the function. v argument is not used. But, it needs to be there so that the second parameter i can be accessed. So, (_, i) is same as (v, i). You can name the parameters with any valid variable name.
You are basically following the following approach
Array.from(arrayLike, (currentValue, index) => { ... } )
So the arrayLike variable needs to be either a string, map ... or it should be an object determining the length of the array (like in your case). So in your case the size of the array is determined by the arrayLike object. currentvalue is undefined every time the arrow function runs and the index goes from 0 to arrayLike.length - 1.
Because it is expecting and arrayLike object and arrays have a length property.

undefined parameter in map() method

I'm trying to understand array functions but all the code examples are confusing for one particular reason. For example, in the code below the x is not defined. What is the role of x and how does this return the array elements multiplied by two?
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
You'd have to spend some time understanding the language structure, the MDN documentation explains it well enough, The "map" function goes through/iterate each and every element in the "array1", where "x" serves as the current value/parameter for each loop, runs the expression with the current value and create a new array containing the computation done by each element being iterated.
I hope this explanation helps you understand it better, you can read further for more depth.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The MDN arrow function documentation covers this quite well.
x => x * 2
is shorthand for
function(x) {
return x * 2;
}
You can read here about map function.
It's taking function foo as argument and returns a new array after applying this function foo on every value in the array.
You can define functions in javascript as follow:
function foo(x) {
return x*2;
}
or like this:
(x) => x*2;
The second form is an arrow function which has no name and can be defined inline.
It's getting argument x and returns x*2, just like function foo.
So map applies this function foo on every value of array1, so x in this case is value in array1.
Instead of writing this:
array1.map(x=>x*2);
You can write:
for(let i in array1) {
array1[i] = foo(array[i]);
}
map has a callback and it has three arguments. It does not matter what you name those arguments. The name of the arguments does not matter, you can name them foo a, b, cat, world, dude, etc.
When it loops over your array it takes whatever is in the index of the array and passes it to the function you provide. So if the first index of your array contains a number, it passes that number.
The second parameter is the index. The third parameter is the array it is looping.
function doubleIt(x, index) {
console.log(x, index)
return x * 2
}
function tripeleIt(foo, index) {
console.log(foo, index)
return foo * 3
}
var myArray = [1,2,3,4]
console.log(myArray.map(doubleIt))
console.log(myArray.map(tripeleIt))
Under the hood map looks like this (I named it xmap so it can run)
Array.prototype.xmap = function (callback) {
var theArray = this;
var theResult = [];
for (var index = 0; index < theArray.length; index++) {
var indexValue = theArray[index];
theResult[index] = callback(indexValue, index, theArray)
// ^^^^^^^^^^ Where the first argument comes from
}
return theResult;
}
var x = [1,2,3,4].xmap(x => x * 2)
console.log(x)

Passing JavaScript arrays to functions

A general question on JavaScript. If I have a function which modifies an array, such as:
var some_array = [];
function a_function(input, array){
//do stuff to array
return array;
}
In most languages:
var some_array = [];
some_array = a_function(input, some_array);
console.log(some_array);
works, however in js the below works as well:
var some_array = [];
a_function(input, some_array);
console.log(some_array);
Is this correct and how is this working?
Arrays in JS are objects and are passed into functions by value, where that value is a reference to the array. In other words, an array passed as an argument to a function still points to the same memory as the outer array.
This means that changing the array contents within the function changes the array passed from outside the function.
function f(arr) {
arr.push(1);
return arr;
}
var array = [];
// both function calls bellow add an element to the same array and then return a reference to that array.
// adds an element to the array and returns it.
// It overrides the previous reference to the array with a
// new reference to the same array.
array = f(array);
console.log(array); // [1]
// adds an element to the array and ignores the returned reference.
f(array);
console.log(array); // [1, 1]

eloquent javascript table drawing function - drawRow arguments

As others have mentioned this code comes from chapter 6 of eloquent javascript. I do not understand where the arguments to the function 'drawRow' are supplied from. If the outer function drawTable were some sort of method it would make sense that it could pass an array as well as the current index, but it's not. It's just an ordinary function so where does it grab 'row' and 'rowNum' from?
function drawTable(rows) {
var heights = rowHeights(rows);
var widths = colWidths(rows);
function drawLine(blocks, lineNo) {
return blocks.map(function(block) {
return block[lineNo];
}).join(" ");
}
function drawRow(row, rowNum) {
var blocks = row.map(function(cell, colNum) {
return cell.draw(widths[colNum], heights[rowNum]);
});
return blocks[0].map(function(_, lineNo) {
return drawLine(blocks, lineNo);
}).join("\n");
}
return rows.map(drawRow).join("\n");
}
Thank you in advance to anyone taking the time to answer this.
According to MDN Array.prototype.map, the callback supplied to map has three parameters: currentValue, index, and array. In this case, when drawRow is supplied to rows.map, row is the currentValue, and rowNum is the index.
Here's a simpler example:
var arr = ['a', 'b', 'c'];
function printArray(value, index) {
console.log(index + ' ' + value);
}
arr.map(printArray);
// prints
// 0 a
// 1 b
// 2 c
The map function calls the provided callback (drawRow in your case) with three arguments for each element of the array that it is iterating over:
the element
the index
the whole array (which you are not making use of here)
You could optionally also set some object to become this inside of the callback.
As others have mentioned, the map method calls drawRow passing the appropriate values. To illustrate the mechanics behind this let's create another function, called M_A_P, from scratch that would also call drawRow with the appropriate values:
function M_A_P (theArray, callback) {
var returnArray = [];
for (var i=0; i<theArray.length; i++) {
var result = callback(theArray[i],i); // note this line
returnArray.push(result);
}
return returnArray;
}
The function M_A_P above calls a function we supply to it with two arguments, the value of one item from theArray (theArray[i]) and the index of the item (i).
Now, we can use M_A_P to process the rows:
M_A_P(rows, drawRow).join("\n");
Remember that the function drawRow will be passed in to M_A_P as the variable callback and the array rows will be passed in as the variable theArray. M_A_P then will call drawRow the way described above with two arguments.
The method Array.map() works similarly but implemented as a method of array objects instead of a function.

Removing elements with Array.map in JavaScript

I would like to filter an array of items by using the map() function. Here is a code snippet:
var filteredItems = items.map(function(item)
{
if( ...some condition... )
{
return item;
}
});
The problem is that filtered out items still uses space in the array and I would like to completely wipe them out.
Any idea?
EDIT: Thanks, I forgot about filter(), what I wanted is actually a filter() then a map().
EDIT2: Thanks for pointing that map() and filter() are not implemented in all browsers, although my specific code was not intended to run in a browser.
You should use the filter method rather than map unless you want to mutate the items in the array, in addition to filtering.
eg.
var filteredItems = items.filter(function(item)
{
return ...some condition...;
});
[Edit: Of course you could always do sourceArray.filter(...).map(...) to both filter and mutate]
Inspired by writing this answer, I ended up later expanding and writing a blog post going over this in careful detail. I recommend checking that out if you want to develop a deeper understanding of how to think about this problem--I try to explain it piece by piece, and also give a JSperf comparison at the end, going over speed considerations.
That said, **The tl;dr is this:
To accomplish what you're asking for (filtering and mapping within one function call), you would use Array.reduce()**.
However, the more readable and (less importantly) usually significantly faster2 approach is to just use filter and map chained together:
[1,2,3].filter(num => num > 2).map(num => num * 2)
What follows is a description of how Array.reduce() works, and how it can be used to accomplish filter and map in one iteration. Again, if this is too condensed, I highly recommend seeing the blog post linked above, which is a much more friendly intro with clear examples and progression.
You give reduce an argument that is a (usually anonymous) function.
That anonymous function takes two parameters--one (like the anonymous functions passed in to map/filter/forEach) is the iteratee to be operated on. There is another argument for the anonymous function passed to reduce, however, that those functions do not accept, and that is the value that will be passed along between function calls, often referred to as the memo.
Note that while Array.filter() takes only one argument (a function), Array.reduce() also takes an important (though optional) second argument: an initial value for 'memo' that will be passed into that anonymous function as its first argument, and subsequently can be mutated and passed along between function calls. (If it is not supplied, then 'memo' in the first anonymous function call will by default be the first iteratee, and the 'iteratee' argument will actually be the second value in the array)
In our case, we'll pass in an empty array to start, and then choose whether to inject our iteratee into our array or not based on our function--this is the filtering process.
Finally, we'll return our 'array in progress' on each anonymous function call, and reduce will take that return value and pass it as an argument (called memo) to its next function call.
This allows filter and map to happen in one iteration, cutting down our number of required iterations in half--just doing twice as much work each iteration, though, so nothing is really saved other than function calls, which are not so expensive in javascript.
For a more complete explanation, refer to MDN docs (or to my post referenced at the beginning of this answer).
Basic example of a Reduce call:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
// if condition is our filter
if (iteratee > 1) {
// what happens inside the filter is the map
memo.push(iteratee * 2);
}
// this return value will be passed in as the 'memo' argument
// to the next call of this function, and this function will have
// every element passed into it at some point.
return memo;
}, initialMemo)
console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
more succinct version:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Notice that the first iteratee was not greater than one, and so was filtered. Also note the initialMemo, named just to make its existence clear and draw attention to it. Once again, it is passed in as 'memo' to the first anonymous function call, and then the returned value of the anonymous function is passed in as the 'memo' argument to the next function.
Another example of the classic use case for memo would be returning the smallest or largest number in an array. Example:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.
An example of how to write your own reduce function (this often helps understanding functions like these, I find):
test_arr = [];
// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
// if we did not pass in a second argument, then our first memo value
// will be whatever is in index zero. (Otherwise, it will
// be that second argument.)
const initialMemoIsIndexZero = arguments.length < 2;
// here we use that logic to set the memo value accordingly.
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
// here we use that same boolean to decide whether the first
// value we pass in as iteratee is either the first or second
// element
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
// memo is either the argument passed in above, or the
// first item in the list. initialIteratee is either the
// first item in the list, or the second item in the list.
memo = reduceFunc(memo, this[i]);
// or, more technically complete, give access to base array
// and index to the reducer as well:
// memo = reduceFunc(memo, this[i], i, this);
}
// after we've compressed the array into a single value,
// we return it.
return memo;
}
The real implementation allows access to things like the index, for example, but I hope this helps you get an uncomplicated feel for the gist of it.
That's not what map does. You really want Array.filter. Or if you really want to remove the elements from the original list, you're going to need to do it imperatively with a for loop.
Array Filter method
var arr = [1, 2, 3]
// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })
// ES2015 syntax
arr = arr.filter(item => item != 3)
console.log( arr )
You must note however that the Array.filter is not supported in all browser so, you must to prototyped:
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /*, thisp*/)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var res = new Array();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
{
var val = this[i]; // in case fun mutates this
if (fun.call(thisp, val, i, this))
res.push(val);
}
}
return res;
};
}
And doing so, you can prototype any method you may need.
TLDR: Use map (returning undefined when needed) and then filter.
First, I believe that a map + filter function is useful since you don't want to repeat a computation in both. Swift originally called this function flatMap but then renamed it to compactMap.
For example, if we don't have a compactMap function, we might end up with computation defined twice:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.filter(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
return isIncluded;
})
.map(x => {
let computation = x / 2 + 1;
return `${x} is included because ${computation} is even`
})
// Output: [2 is included because 2 is even, 6 is included because 4 is even]
Thus compactMap would be useful to reduce duplicate code.
A really simple way to do something similar to compactMap is to:
Map on real values or undefined.
Filter out all the undefined values.
This of course relies on you never needing to return undefined values as part of your original map function.
Example:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.map(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
if (isIncluded) {
return `${x} is included because ${computation} is even`
} else {
return undefined
}
})
.filter(x => typeof x !== "undefined")
I just wrote array intersection that correctly handles also duplicates
https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0
// array intersection that correctly handles also duplicates
const intersection = (a1, a2) => {
const cnt = new Map();
a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
return a1.filter(el => el in cnt && 0 < cnt[el]--);
};
const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]
First you can use map and with chaining you can use filter
state.map(item => {
if(item.id === action.item.id){
return {
id : action.item.id,
name : item.name,
price: item.price,
quantity : item.quantity-1
}
}else{
return item;
}
}).filter(item => {
if(item.quantity <= 0){
return false;
}else{
return true;
}
});
following statement cleans object using map function.
var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);

Categories