What’s wrong with my map version for a JS array?

Asked

Viewed 329 times

6

var kiko = [1,2,3,4,5,6];

var forEach = function(array, newArray, action){
     for (var i=0; i<array.length; i++){
        var newArray = [];
        newArray.action(array[i]);
    };   
};

forEach(kiko, newKiko, push)
newKiko

I’m studying abstract functions, and I came across an example from the book Eloquent Javascript in which a version of array.map is created from scratch. I tried to make mine, which scans an array, and creates another array for each item you go through. I am getting "Push is not defined" error but it is a native method of arrays in JS. What may be going wrong?

I tried a second variation, but it gives me an empty array:

var kiko = [1,2,3,4,5,6];

var forEach = function(array, action){
    for (var i=0; i<array.length; i++){
        action(array[i]);
    };   
};


forEach(kiko, function(element){  
    var newKiko = [];
    newKiko.push(element);
})

newKiko
  • push is a function of Array, is not a global function. http://www.w3schools.com/jsref/jsref_push.asp

  • newKiko is an array! It does not work with it?

4 answers

6

The other answers are very good, but I missed saying one thing: why the hell do you want to create a function of map and calls her forEach? They are two different things! And it is very important that the name of the function represents well what it does. The idea of a foreach is just iterate over a list, like the function of the maicon’s response. If your intention is to map an array to another, call the map even.

About the implementation, I quite like the book version that you quoted (great book, by the way):

function map(array, transform) {
  var mapped = [];
  for (var i = 0; i < array.length; i++)
    mapped.push(transform(array[i]));
  return mapped;
}

Note that it does not receive the output array, it creates this array and returns. Maybe you have an earlier C formation where it is common to pass a pointer to an output array, but in JS this would rarely be a good solution.

5

To answer your question of what’s failing your code:

1-your function does not return anything, it is running half nonsense. After the cycle for you have to give return newArray;

2- the method push is an array method and not available in the global scope. You can "copy" it but you have to call Array.prototype.push. Then when you use it, you use it .call) thus:

function teste(arr, action) {
    action.call(arr, 3);
}
var foo = [1, 2];
teste(foo, Array.prototype.push);
console.log(foo); // dá [1, 2, 3]

3- when you use var newArray = []; within the cycle for you are deleting the array and starting again. You can remove that line.

The corrected code could be like this: http://jsfiddle.net/chqv0zxy/

var kiko = [1, 2, 3, 4, 5, 6];
var forEach = function (array, newArray, action) {
    for (var i = 0; i < array.length; i++) {
        action.call(newArray, array[i]);
    };
    return newArray;
};

var newKiko = forEach(kiko, [], Array.prototype.push)
console.log(newKiko); // [1, 2, 3, 4, 5, 6]

or if you don’t use return but passing the array by reference: http://jsfiddle.net/chqv0zxy/1/

var kiko = [1, 2, 3, 4, 5, 6];
var forEach = function (array, newArray, action) {
    for (var i = 0; i < array.length; i++) {
        action.call(newArray, array[i]);
    };
};
var newArray = [];
forEach(kiko, newArray, Array.prototype.push)
console.log(newArray); // [1, 2, 3, 4, 5, 6]

3

In the first example the function push is not defined because it is exclusive to Array. It is not a global function.

In your second example the error is here:

forEach(kiko, function(element){  
    var newKiko = []; /* <-- aqui */
    newKiko.push(element);
})

You are clearing the array every time it passes on for. The right thing would be:

var kiko = [1,2,3,4,5,6];
var newKiko = []; /* coloca aqui */

var forEach = function(array, action){
    for (var i=0; i<array.length; i++){
        action(array[i]);
    };   
};


forEach(kiko, function(element){  
    newKiko.push(element);
})

newKiko
  • Thank you very much Maicon! Is there no way I can get the function itself to create this new array? So I wouldn’t always depend on the empty array statement before setting it?

  • @Kvera But what would he do with this new array that the function created? Would it save to a global one? (Argh!) To do this is easy, just put one if inside the function and, if the variable is undefined or null, create, but not create. But where to store the result, this is the X of the question (so I am of the opinion that an explicit statement is better).

  • I share the same opinion as @mgibsonbr

3


In your first code, you are trying to choose a method dynamically, but the operator . is not the right way to do it. When you do objeto.algo it always looks for a property/method named algo, it doesn’t matter whether or not there is a variable called like that. To choose an object method from its name, it is necessary to use strings and brackets:

var action = "push";
...
newArray[action](...);

This gives you some flexibility, provided the pair (newArray, action) is consistent (i.e. action always has the name of a newArray that receives an argument). On the other hand, this forces you to always use an object, you cannot use an isolated function.

His second code, on the other hand, is perfect (the definition, not the call, as pointed out by the other answers), that is, perfect for a forEach (for a map, see the response of the bfavaretto). If you want to call with an isolated function, you call, if you want to call with a method of an object, you "connect" that method to the object through the function bind (or perhaps with some other kind of currying):

var newKiko = [];

var push = Array.prototype.push;
// ou:
var push = newKiko.push;

var boundPush = push.bind(newKiko); // Fixa o "this" da função

...

boundPush(10); // Faz o mesmo que: newKiko.push(10)

Examples:

var kiko = [1,2,3,4,5,6];

var forEach = function(array, action){
    for (var i=0; i<array.length; i++){
        action(array[i]);
    };   
};

// Chamando com uma função qualquer
var newKiko = [];
forEach(kiko, function(element){  
    newKiko.push(element);
})
document.body.innerHTML += "<p>" + JSON.stringify(newKiko) + "</p>";

// Chamando com um método de um objeto
var newKiko2 = [];
forEach(kiko, newKiko2.push.bind(newKiko2));
document.body.innerHTML += "<p>" + JSON.stringify(newKiko2) + "</p>";

And by the way, the error in the call I mentioned earlier was re-creating the list newKiko within the function used as callback - the first example above shows the correct way to call the code you wrote.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.