Creating my own array.prototype.map method. How can I access the array?
Asked Answered
D

10

5

So I am trying to create a method that mimic exactly what the Array.prototype.map() method does and there is a lot I am confused about. The main problem I suppose comes down to its syntax. I know there are many different ways to utilitize the map method for instance:

example 1:

let say I have an array of objects -

var movieList = [
   {"Title" : "Inception",
   "Awards" : "4 oscars",
   "Language" : "English",
   "imdbRating" : "8.8"
   },

   {"Title" : "Inception2",
   "Awards" : "44 oscars",
   "Language" : "English and Spanish",
   "imdbRating" : "9.8"
   },

   {"Title" : "Interstellar",
   "Awards" : "10 oscars",
   "Language" : "English",
   "imdbRating" : "9.5"
   }
];

Lets say I want to make a function that returns a list of objects that contains only the title of the movie and its imdbRating. In this case, I can use the map() method:

let newList = movieList.map( (current) ({'title': current['Title'],     
               'rating': current['imdbRating'] }) );

the above line of code satisfies what i need to achieve my objective using the map method. However, the syntax can be different for other cases

example 2:

let s = [1, 2, 3, 4];

let s2 = s.map( function(item) {
   return item*2;
});

using the map function s2 will return an array that has for each element double the value of each element in the s array. Now, going back to the theme of this post, the problem I am working on gave me this outline to start with:

Array.prototype.myMap = function(callback) {
   let newArray = [];

I understand that when an array calls on the myMap method, it is inserting 2 arguments, the array and a function. But I cannot wrap my head around how I can concretely assign the value of each callback function on the element to the newArray in the myMap method. Especially because I do not know how to access the original array.

One of my attempts that I know is ridiculous because I do not know how I can access the length of the array as well as the array itself which calls on the myMap method-

Array.prototype.myMap = function(callback) {
   let newArray = [];
   let x = this.length();
   for(let i=0; i<x; i++){
       let counter = callback();
       newArray.push(counter);
   }
   return newArray;
};

the way I've understood the map() method thus far, is it takes 3 arguments, an array, a function, and the element that will be put thru the function and I do not know the syntax well enough to iterate over the array that calls on the map method and nowhere in my course have I been taught how to do this and I have not found any online resource either that offers a solution to this problem.

Dropsonde answered 19/6, 2019 at 5:46 Comment(2)
what is Array.myMap supposed to be? and why would it have a function called length? seems you wanted this.length - or you can study a Array#map polyfillOtherworldly
yeah i tried this.length and it wouldn't work. that was just my last-ditch attempt at trying to figure the length of the array that calls on the methodDropsonde
W
8

length is not a method - it's just a property. And you need to pass this[i] to the callback for the correct output.

Array.prototype.myMap = function(callback) {
  let newArray = [];
  let x = this.length;
  for (let i = 0; i < x; i++) {
    let counter = callback(this[i]);
    newArray.push(counter);
  }
  return newArray;
};

let arr = [1, 2, 3];
arr = arr.myMap(e => e * 2);
console.log(arr);

Please note it's quite bad practice to mutate the prototype methods - and especially creating a new one when an identical method exists. (map has all the functionality of myMap plus more).

Whity answered 19/6, 2019 at 5:58 Comment(5)
yes it works. thank u so much. i completely forgot about the this.property syntax... i was stressing over so much about the function and all the components that make up the map method that i completely overlooked the simplest syntax that solved my problem.Dropsonde
map doesn't visit non–existent or deleted properties within the index range 0 to initial length - 1, but this version will. There needs to be a hasOwnProperty test before processing values.Mcdaniels
@Mcdaniels What do you mean? Just an if statement before the call-back? And if so, what would the value for that item be? Would a filter be required?Whity
Missing items in the initial array are also missing from the map, they aren't visited at all. E.g. [0,,,3].map(fn) only visits elements 0 and 3 since 1 and 2 don't exist, so they are also missing in the result. So you need to test if the element exists (say this.hasOwnProperty(i)) and then assign to the index rather than using push.Mcdaniels
Looks pretty good but you also need to take care about the i (index), so calling the callback should look like: callback(this[i], i, this).Floatplane
O
3

This is simplified version of the actual map Polyfill. You need to get the length using this.length. And pass the current item in loop, it's index, and the array itself as a parameter to callback

Array.prototype.myMap = function(callback, thisArg) {
  const newArray = [];
  const length = this.length;

  for (let i = 0; i < length; i++) {
    let value = callback(this[i], i, this); // call with proper arguments
    newArray.push(value);
  }

  return newArray;
};

const arr = [1, 2, 3];

console.log(arr.myMap(a => a * 2))

Note: map method also takes a thisArg as parameter. If you want to use that, you need to call the callback with thisArg as this

callback.call(thisArg, this[i], i, this);
Oliguria answered 19/6, 2019 at 5:57 Comment(0)
S
1

Like this answer, but correctly preserves empty items.

Array.prototype.map = function (callback, thisArg) { 
    let arr = [];
    const len = this.length;
    for (let i = 0; i < len; i++) {
        if (i in this) arr.push(callback.call(thisArg, this[i], i, this));
        else arr.length++;
    }
    return arr;
};
Shah answered 6/12, 2022 at 9:37 Comment(5)
I like this, but would it not be more efficient just to do new Array(this.length) up front instead?Leodora
You mean instead of let arr = []; ?Shah
And incrementing the length every iteration, yes.Leodora
Something like thisLeodora
Seems better quite frankly. I can't think of a use case in which initializing the array length would be a problem.Shah
C
0

Here is where you can learn about javascript map function more: https://www.w3schools.com/jsref/jsref_map.asp

Implementing map method is actually quite easier than you think. Here is a really simple example that would do exactly what the map method do from https://www.w3schools.com/jsref/jsref_map.asp:

Array.prototype.myMap = function(callback) {
    arr = [];
    for (var i = 0; i < this.length; i++)
        arr.push(callback(this[i], i, this));
    return arr;
};

//tests
var numbers2 = [1, 4, 9];

var squareRoot = numbers2.myMap(function(num) {
    return Math.sqrt(num);
});

console.log(squareRoot); // [ 1, 2, 3 ]

So, the map method simply returns an array by whose values map to the original array's values by running a specified function given as an argument on each of the values.

Note: The method skips the optional parameter of the original map function which specifies what will be the this value

Connie answered 19/6, 2019 at 6:4 Comment(0)
G
0

Here is a working version of your myMap function.

A part of imitating these methods that may confuse people is how the this keyword works in javascript. Here is an MDN Reference article.

Array.prototype.myMap = function(callback, thisArg) {
  let newArray = [];
  let x = this.length;
  for(let i=0; i<x; i++){
    let counter = callback.call(thisArg, this[i], i, this);
    newArray.push(counter);
  }
  return newArray;
};
Gradate answered 19/6, 2019 at 6:11 Comment(0)
G
0
Array.prototype.mappy = function (callback, thisArg) { 
    let newArray = [];
    for (let i = 0; i < this.length; i++) {
        if (i in this) newArray.push(callback.call(thisArg, this[i], i, this));
        else newArray.push(undefined);
    }
    return newArray;
};
Geosyncline answered 19/9, 2022 at 18:15 Comment(4)
What is the point of i in this? Also, please take the time to format your post properly.Leodora
@GeneralGrievance The point of i in this is to handle empty array indexes. For example, If you try to make an arithmetic operation with an empty array index, is returns NaN, instead of undefined.Shah
@ManosKaparos True, but then pushing undefined doesn't really make sense here as the real map preserves empty items. Probably the real way to do it is to create the new array with the original length, then copy only when i in this is true.Leodora
I completely agree in the first statement. Since NaN can represent an undefined or an unpresentable value, hence an empty array index. The thing is that, in this case, the original prototype map method returns neither undefined nor 'NaN' but an empty element instead. Regarding the creation of the new array, I believe that it is situational since you can no longer compare old array with the new one, based on index values and length. Check out my answer: stackoverflow.com/a/74700247Shah
W
0

You could take advantage of the spread operator and do something like this:

Map.prototype.slice = function(start, end) {
    return new Map([...this].slice(start, end));
};

And in use:

const testMap = new Map([["a", 1], ['b', 2], ["c", 3], ["d", 4], ["e", 5]]);
console.log(testMap); // Map(5) { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }
console.log(testMap.slice(0, 2)); // Map(2) { 'a' => 1, 'b' => 2 }

You can definitely build upon this since this still has the caveats of Array.prototype.slice().

Writeup answered 21/10, 2022 at 22:57 Comment(0)
S
0

You can also use forEach if you don't want to use a for loop

Array.prototype.myMap = function (callback) {
  let newArr = [];
  this.forEach((item) => {
    let elem = callback(item);
    newArr.push(elem);
  });
  return newArr;
};
Stav answered 10/1, 2023 at 3:0 Comment(0)
S
0

Creating a custom map function in javascript

 Array.prototype.myMap = function (cb){
    let temp =[];
    for(let i = 0; i < this.length; i++){
    temp.push(cb(this[i], i, this))
    }
    return temp;
    };

In this custom function callback cb having three arguments is current element, index and actual array.

const arr = [1, 2, 3, 4];
//using myMap
arr.myMap((num)=>num)
Somehow answered 14/3 at 9:4 Comment(0)
K
0

my proposition on how to doing with recursive way with typescript

Array.prototype.customMap = function customMap<T>(
  callback: (element: T, index: number, array: T[], self?: T[]) => T
): T[] {
  if (this.length === 0) {
    return [];
  }
  return (([index, result]) => {
    return [
      callback(
        this[this.length - 1 - index],
        this.length - 1 - index,
        this,
        this
      ),
      ...result,
    ];
  })(mapWithSize<T>(this.slice(1), callback));
};


function mapWithSize<T>(
  sequence: T[],
  callback: (element: T, index: number, array: T[], self?: T[]) => T
): [number, T[]] {
  return [sequence.length, sequence.customMap(callback)];
}
Kumiss answered 27/3 at 23:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.