[totally lost] Implement map on a Prototype

Hi all,

I’m struggling with the exercise “Implement map on a Prototype” and would be most grateful for any help from the community, thanks in advance.

Link to the exercise:

This is my code:

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


  // Only change code below this line
  for (let i = 0; i < this.length; i++) {
    tempVariable = callback(this[i]);
    newArray.push(tempVariable);
  }
  // Only change code above this line


  return newArray;
};

From my understanding, “this” refers to the object that is calling the method, ie whichever array is calling the method, eg [23, 65, 98, 5, 13] or [“naomi”, “quincy”, “camperbot”] etc.

I’m saying iterate through the elements in the array. Then call whatever callback function is passed in, and I’ll pass into that callback function this[i]. I then push the value into the new array.

The weird thing is, if I try console logging the following in my own code editor:

console.log([23, 65, 98, 5, 13].myMap(item => item * 2));
console.log(["naomi", "quincy", "camperbot"].myMap(element => element.toUpperCase()));
console.log([1, 1, 2, 5, 2].myMap((element, index, array) => array[index + 1] || array[0]));

The first two produces what the challenge asks me. The last console.log throws an error.

But what’s weird is if I put the code I have into the fCC challenge, it says I got all of it wrong (except the requirement “Your code should not use the map method.”)

I would be most grateful if someone could point me in the right direction. I think part of the problem is I also don’t really even understand what this part means:

[1, 1, 2, 5, 2].myMap((element, index, array) => array[index + 1] || array[0])

It seems the callback accepts multiple arguments “element, index, and array” and but I can’t get my head around the last part.

Any help would be greatly appreciated, thanks.

1 Like

Javascripts array MAP function does have the option to specify the three variables. Element being the current array item, index being that items location in the array( its index number), and lastly array is the array as a whole. So that last problem takes those three variables, and takes the array item after the current item, and performs an or operation(||) with the first item in the array.

So it would seem that your myMap function would need to be able to accept three elements in order to support that last problem.

1 Like

It is not that the myMap needs to be able to accept 3 arguments. myMap only should accept one argument (a callback function).

The key to solving this challenge is to understand the the map function is that the callback function can take 3 arguments (the first being the element being iterating over, the second being of the element being iterated over, and the third being the original array on which the method was called on.

The instructions states:

The Array instance can be accessed in the myMap method using this .

Your current solution handles passing the element being iterated over (the 1st argument) to the callback function. You just need to figure how to pass the current index of the element being iterated over (2nd argument), and the array instance itself (the 3rd argument).

1 Like

Yeah, I definitely didn’t word that the greatest.

1 Like

@kinome79 and @RandellDawson thanks so much for the responses, really appreciate it.

I need some time to get my head around the feedback you’ve provided. I’m still not sure how to pass multiple arguments into the callback, I was trying the following and it didn’t work:

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


  // Only change code below this line
  for (let i = 0; i < this.length; i++) {
    tempVariable = callback(this[i], i, this);
    newArray.push(tempVariable);
  }
  // Only change code above this line


  return newArray;
};

Could you provide some more hints? Thanks in advance.

Is tempVariable really supposed to be a global? You don’t really need it anyway. Those two lines can be combined into one.

1 Like

Was wondering why you said it was failing the other tests earlier… tempVar isn’t declared correctly.

1 Like

@bbsmooth and @kinome79 thanks for the comments.

@kinome79, you’re exactly right, I didn’t even declare the variable correctly! Thanks for pointing that out.

@bbsmooth, I don’t know why I often default to declaring a new variable, it’s not the most efficient, and you’re right, I can combine the two lines into one. I did that and it worked! Like this:

newArray.push(callback(this[i], i, this));

Sorry, I was wondering if I could ask two more questions as I’m still a bit confused about the parameters in the callback. I just want to make sure I understand things 100%.

**Question 1:

Would it be fair to say if we didn’t need to call:

[1, 1, 2, 5, 2].myMap((element, index, array) => array[index + 1] || array[0])

…then the code above could simply be:

newArray.push(callback(this[i]));

**Question 2:

Also, wouldn’t it make more sense to not use the term “element” and simply use “item” for the first two calls? Like:

.myMap(item => item * 2)
.myMap(item => item.toUpperCase()) // instead of .myMap(element => element.toUpperCase())

In either case, only one argument is passed into the callback, so in both those cases, we will pass in “this[i]” to the callback as we iterate through the loop.

Thanks again for all the help.

element makes more sense here since it accurately describes what the value is. It represents the current element of the array being iterated over.

1 Like

@RandellDawson, thanks for the response!

I was thinking in both those cases, we are basically looking at each individual element in the array, ie this[i]. We either multiply that element by 2, or we change that element (ie string) to uppercase.

Wouldn’t it make more sense to use one term? We could use the word “element” in both cases like:

.myMap(element => element * 2)

and

.myMap(element => element.toUpperCase())

Rather than:

.myMap(item => item * 2)

and

.myMap(element => element.toUpperCase())

It just seems less confusing to me. Sorry if I’m completely missing something. Would you be able to shed some light on why or whether it is necessary to use two different terms? Thanks in advance.

Glad you got it working. And yes, if you didn’t need to support that last case callback(this[i]) would have been sufficient, but then it wouldn’t be an accurate copy of array.prototype.map because map supports being able to use all three.

Regarding the naming of the passed variables, it would work no matter what you call them, and what you call them is at the discretion of the programmer, just like in your for loop you used i where I typically use x… in this case whoever wrote these may have made them different on purpose to illustrate this point, or maybe just oversite… if you wanted,

.myMap(dog => dog.toUpperCase())

would work, as would

.myMap( (dog, dog_number, all_the_dogs) => all_the_dogs[dog_number + 1] || all_the_dogs[0])

, but you may get nasty reactions from whoever needs to read your code. There are certain naming conventions and recommendations, but like any other variable, select what makes sense to you.

1 Like

Internally and in the documentation for map naming the parameter element makes sense, as that is what it is, the current element in the array.

But the consumer of the map API should name the parameter whatever best describes what the element actually is. If it was an array of numbers you might just call it number, if it is a specific type of number there is usually a name for it, like age, or height, or whatever best describes what the element actually is.

2 Likes

@kinome79 and @lasjorg , thanks so much for the responses!

This is excellent, thank you. This makes things clear now. I initially thought the fCC challenge was just trying to get us to kind of replicate “map” (so we can understand the built-in function), I didn’t realize it was trying to get us to make an accurate copy of array.prototype.map. I also didn’t realize “map” supported all three parameters, this makes sense now, thanks again.

Thank you! I may have been overthinking “item” vs “element”. I think initially when I first looked at the challenge I was confused and kind of thought “item” and “element” were two different arguments that can be passed in. This initial confusion led me to write the previous post asking why bother with two different names if they’re the same thing (ie a placeholder for what can be passed in).

I see now that being more specific, like using “age” or “height” can be even better than using one term throughout. It makes sense, thank you!!

It is not a complete copy of Array.prototype.map because the real one accepts a 2nd argument (the first being the callback function), that lets you set the value of this for the cal;back function.

1 Like

@RandellDawson, I see, thanks for clarifying. Appreciate all the help.

I’ve been lurking on this thread because this challenge had totally stumped me. Thanks to @blackpug for doing all the hard work.

Blockquote It is not a complete copy of Array.prototype.map because the real one accepts a 2nd argument (the first being the callback function), that
lets you set the value of this for the calback function.

So in this situation would the second argument be this input as
.map(callback, this) ?

Thank you.

1 Like

Well, for the implementation, I think you want to bind or call with the this context passed.

Here is an old version of a custom map I had lying around. It is not implemented on the prototype but is just a stand-alone function.

function map(originalArray, callback, optionalThisObject) {
  const mappedArray = [];
  let mappedCallback = callback;

  if (optionalThisObject) {
    mappedCallback = callback.bind(optionalThisObject);
  }

  for (let i = 0; i < originalArray.length; i++) {
    if (i in originalArray) {
      mappedArray[i] = mappedCallback(originalArray[i], i, originalArray);
    }
  }
  return mappedArray;
}

Don’t ask me why it is written the way it is because I don’t remember.

1 Like

Not sure how useful it is, but I did find the following example of using the second parameter on StackOverflow:

var numbers = [1,2,3,4,5,6,7,8,9,10];

function callback(element) {
  return element + this.add;
};

var mapped = numbers.map(callback, {
  add: 10
});

console.log(mapped); // [11,12,13,14,15,16,17,18,19,20]

NOTE: You can reference this successfully inside a non-arrow function.

1 Like