3

In Python, you can something like this:

>>> list(map(str.upper, ['foo','bar']))
['FOO', 'BAR']

I would like to be able to do something similar in javascript:

I've tried the following in Chrome using the native map implementation ( https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map ):

['foo','bar'].map(String.prototype.toUpperCase)

['foo','bar'].map(String.prototype.toUpperCase.call)

Why doesn't call work? Is there any elegant way to do this or must I wrap toUpperCase in a callback function? Thx

1
  • toUpperCase.call doesn't work because call is resolved against Function.prototype and so is not related to toUpperCase. It looks at this to figure out which method is being called, and the way map invokes the function, this is undefined. Commented Nov 12, 2010 at 18:39

5 Answers 5

2

Try:

['foo','bar'].map(Function.prototype.call.bind(String.prototype.toUpperCase))

or for the same effect:

['foo','bar'].map(Function.call.bind("".toUpperCase))

JavaScript's treatment of this is a bad trip.

Sign up to request clarification or add additional context in comments.

Comments

1

Modern browsers support this (although it is a recent addition)

var a = ['foo','bar'];
var b = a.map(String.toUpperCase);

alert(b[1]);

or even

var a = ['foo','bar'].map(String.toUpperCase);
alert(a[0]);

example: http://www.jsfiddle.net/xPsba/

3 Comments

That works in firefox 4 but not in chrome 7 on mac. Guess I'll have to wrap it in a function :)
This won't work on other browser than Firefox because String.toUpperCase is a Mozilla-extension, not part of the ECMAScript Standard (note that can be wrongly confused with String.prototype.toUpperCase), they call those "static" methods "Array and String Generics" and I don't really think other implementors will include them....
your JSFiddle snippet is using MooTools, put No Library and try on other browsers. ;)
1

That is because String.prototype.toUpperCase runs on this (Context) object but map function can't set the array element as context. It passes it as an argument. A workaround would be

['foo','bar'].map(function(k) { return String.prototype.toUpperCase.call(k) });

1 Comment

even better is to skip the prototype part altogether and use it as ['foo','bar'].map(String.toUpperCase); which runs in the right context.
1

You're all missing the point. :-)

We want to call a method (not a function) on all elements of an array. You can use map:

a.map(function(x){x.frob()})

but that's too much typing. We want to say:

a.mapm(A.frob)

where A is the class of the elements of a and mapm is a new Array method that calls its argument method on each element of this (array a in this case). You could define mapm thus:

Array.prototype.mapm = function (method)
{
    return this.map(function (x) { return method.apply(x) } )
};

and call it thus:

["a", "b", "c"].mapm("".toUpperCase)    ==>     ["A", "B", "C"]

The only problem is that you've added a new element, mapm to every array, though it's ignored by most Array methods, e.g. length and map.

1 Comment

+1 for understanding the point. Of course, in cases where your code will need to play nicely with other code, modifying Array's prototype is risky, so maybe a slight rework to allow something like:a.map(method("toUpperCase")); function method(m){return function(x){return x[m]();}} Plus, a minor enhancement could also allow you to pass some parameters for the method via method
-1

You can use something like this:

function map(arr, fn){
    var i = 0, len = arr.length, ret = [];
    while(i < len){
        ret[i] = fn(arr[i++]);
    }
    return ret;
}

And to use it:

var result = map(['foo', 'bar'], function(item) {
    return item.toUpperCase();
});

console.log ( result ); // => ['FOO', 'BAR']

6 Comments

Do you have any example usage? I would rather use the native map function though.
It is not broadly available yet
a for loop would be a lot more simpler :p
Not sure for is any simpler, but clearly more conventional ;)
@DenisHowe, at the time of the answer there were no native map functions in most browsers
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.