brabec
brabec

Reputation: 4730

javascript - Array.map with String.trim

Why doesn't the following work? (Chrome, so no issues with Arrays.map missing)

[" a ", "b", " c", "d "].map(String.prototype.trim)

TypeError: String.prototype.trim called on null or undefined

Upvotes: 14

Views: 18482

Answers (5)

Cerbrus
Cerbrus

Reputation: 72857

map passes each element of the array as parameter to the function:

[element1, e2].map(myFunction); // --> myFunction(element1); myFunction(e2)

String.prototype.trim is not a function that you pass a string to be trimmed. You call the function as a method of that string, instead:

" some string ".trim(); // "some string"

To use trim in a .map, you'll need to do something like:

[" a ", "b", " c", "d "].map(e => e.trim());

Upvotes: 30

Learn how this works.


Do either

[" a ", "b", " c", "d "]
.map(Function.prototype.call.bind(String.prototype.trim))

or

[" a ", "b", " c", "d "].map(fetch.call.bind("".trim))
// .map(<a whatever function>.call.bind(<a whatever string>.trim))

or simply

[" a ", "b", " c", "d "].map(s => s.trim())

.


Why not—

….map(String.prototype.trim)?

Because the trim function trims this, not its argument. It does not have any parameters. You do " no ".trim() (" no " being the this), not trim(" no ").

….map(String.prototype.trim.call) when the call forwards the first argument as this?

Because, in this example, the function which the call function forwards to, is not String.prototype.trim, but undefined. The call function forwards to its this, and in this example, the call function is called without this, as if you do (undefined).call(…).

This example is the same as doing:

const _call = String.prototype.trim.call;
// _call has no `this`.
….map(_call)

and in this case, the _call function’s this is not String.prototype.trim, unlike the direct call String.prototype.trim.call(…) (in the form of <this>.<function>()). The _call variable is a mere function value that does not retain String.prototype.trim as its this.

For the call function to retain String.prototype.trim as its default this, you need to use the Function.prototype.bind function to bind the specific this, String.prototype.trim, to it. The bind function also forwards arguments.

const call_without_this = Function.prototype.call;
const trim_without_this = String.prototype.trim;
const call_with_trim_as_its_this =
    call_without_this.bind(trim_without_this);
….map(call_with_trim_as_its_this)

The function returned by the bind function call, call_with_trim_as_its_this, is a function that calls the call function with this of trim_without_this and arguments being forwarded, that is, a function that calls trim_without_this with this of the first argument.

And also, since properties in the prototype of a class are inherited by instances of the class, <class>.prototype.<property> can be replaced with <the class’ instance>.<property>.

const call_without_this = fetch.call;
const trim_without_this = "".trim;
const call_with_trim_as_its_this =
    call_without_this.bind(trim_without_this);
….map(call_with_trim_as_its_this)

Upvotes: 3

greuze
greuze

Reputation: 4398

One shorter version with an arrow function:

[" a ", "b", " c", "d "].map(e => e.trim());

Upvotes: 9

Nick
Nick

Reputation: 156

With Ramda (functional programming library for Javascript):

> var R = require('ramda')
undefined
> [" a ", "b", " c", "d "].map(R.trim)
[ 'a', 'b', 'c', 'd' ]

This example was made using the built-in node repl.

Upvotes: -2

Eternal1
Eternal1

Reputation: 5625

That's actually because Array.map() function should have a currentElement as an argument, while String.prototype.trim doesn't take any arguments, therefore we can't call it that way. So, you'll have to do it hard way:

[" a ", "b", " c", "d "].map(function(elem){
     return elem.trim();
});

Upvotes: 5

Related Questions