Reputation: 87203
I have an array
var arr = [' A ', ' b ', 'c'];
and I want to trim
the spaces from each of the element from array.
It can be done by using Array.map
as
arr.map(function(el) {
return el.trim();
});
I'm curious about passing the trim
/toLowerCase
function directly to the map
as callback function, like arr.map(Math.max.apply.bind(Math.max, null)); to get the maximum element from each subarray or arr.map(Number); to cast each element to Number.
I've tried
arr.map(String.prototype.trim.apply);
but it is throwing error
Uncaught TypeError: Function.prototype.apply was called on undefined, which is a undefined and not a function
I expect that String.prototype.trim.apply
should be called for each element in the array with the context set to the element from array(passed to apply
);
I've also tried different combinations of apply
, call
and bind
with no success.
map
map
Upvotes: 16
Views: 1824
Reputation: 135277
I use a small method
function for this kind of thing -
const method = f => f.call.bind(f)
const list = [
" alice ",
" bob ",
" charlie "
]
console.log(list.map(method("".trim)))
Another option is to define trim
in advance -
const method = f => f.call.bind(f)
const trim = method("".trim)
const list = [
" alice ",
" bob ",
" charlie "
]
console.log(list.map(trim))
[
"alice",
"bob",
"charlie"
]
Some methods take additional arguments. method
works with that too -
const method = f => f.call.bind(f)
const trim = method("".trim)
const replace = method("".replace)
const list = [
" alice ",
" bob ",
" charlie "
]
console.log(list.map(trim).map(v => replace(v, "e", "E")))
[
"alicE",
"bob",
"charliE"
]
You can rewrite method
to enable tacit programming (aka point-free style) -
const method = f => (...args) => data =>
f.apply(data, args)
const trim = method("".trim)()
const upper = method("".toUpperCase)()
const replace = method("".replace)
const list = [
" alice ",
" bob ",
" charlie "
]
console.log(list.map(trim).map(replace(/[aeiou]/g, upper)))
[
"AlIcE",
"bOb",
"chArlIE"
]
Finally we can make method
smarter to analyze f.length
to determine if more arguments should be supplied by the caller -
const method = f =>
f.length == 0
? data => f.call(data)
: (...args) => data => f.apply(data, args)
const trim = method("".trim)
const upper = method("".toUpperCase)
const replace = method("".replace)
const list = [
" alice ",
" bob ",
" charlie "
]
console.log(list.map(trim).map(replace(/[aeiou]/g, upper)))
[
"AlIcE",
"bOb",
"chArlIE"
]
Upvotes: 1
Reputation: 43718
arr.map(String.prototype.trim.call.bind(String.prototype.trim));
call
uses this
internally, which must point to the trim
function to work properly in this case. Simply passing String.prototype.trim.call
would leave call
unbound to any method, resulting in the this
value pointing to window
instead.
It works, but when used apply instead of call it throws error, arr.map(String.prototype.trim.apply.bind(String.prototype.trim));
The problem is that map
will pass 2 arguments, the item and the index. Therefore it ends up calling something like 'String.prototype.trim.apply('test', 0)
which fails since the second argument must be an array.
one more thing [' A ', ' B ', 'c'].map(String.prototype.trim.call.bind(String.prototype.toLowerCase));, in this, I've used trim.call and passed toLowerCase as context then why we need trim here, why trim is not called
When using call.bind
the path that you chose to access the call
function reference becomes irrelevant. The function that will get called is the one that is bound.
If you want to compose functions together you will need a different approach:
var call = Function.prototype.call,
trim = call.bind(String.prototype.trim),
toLowerCase = call.bind(String.prototype.toLowerCase),
trimAndLowerCase = pipelineFrom(trim, toLowerCase);
[' TeST '].map(trimAndLowerCase);
function pipelineFrom(fn1, fn2) {
return function (val) {
return fn2(fn1(val));
};
}
However at this point you're better off with:
arr.map(function (val) {
return val.trim().toLowerCase();
});
Upvotes: 14
Reputation: 60788
This works, it sure is long-winded though:
var t = String.prototype.trim.call.bind(String.prototype.trim);
arr.map(t);
Because it's longwinded there are blog posts and modules devoted to uncurrying, which is what you are trying to do here.
I did ask about this here once...
Upvotes: 4