Adding .map() directly to String prototype

# Ben Fletcher (6 years ago)

Consider adding the array .map() implementation directly to the String prototype.

Here is a possible polyfill:

String.prototype.map = function (fn) {
  return Array.prototype.map
.call(this, fn)
.join('');
}

This replaces the common pattern of splitting, mapping, joining strings for processing.

# Rodrigo Carranza (6 years ago)

In fact there should be a common interface that iterables should implement. I think this could have been proposed before. This way there would be not more common methods proposals. On may. 17 2018, at 10:16 am, Ben Fletcher <bfletch at gmail.com> wrote:

Consider adding the array .map() implementation directly to the String prototype.

Here is a possible polyfill:

String.prototype.map = function (fn) { return Array.prototype.map .call(this, fn) .join(''); }

This replaces the common pattern of splitting, mapping, joining strings for processing.

# kdex (6 years ago)

This idea comes from a rather ASCII-centric perspective upon strings and will definitely break multi-byte characters. And even if it wouldn't, your intent might have been to do just that.

Long story short, it's debatable what exactly you want to iterate in a string, hence such a method would lead to pretty bad practice and should therefore not be added to the language.

# devlato (6 years ago)

I think makes sense to have String.prototype.reduce too then.

17.05.2018, 17:16, "Ben Fletcher" <bfletch at gmail.com>:

# T.J. Crowder (6 years ago)

On Thu, May 17, 2018 at 4:24 PM, kdex <kdex at kdex.de> wrote:

This idea comes from a rather ASCII-centric perspective upon strings and

will

definitely break multi-byte characters.

No. One of the improvements to JavaScript since ES5 (you know, those things you keep objecting to) is that strings are now iterable, and the iteration visits characters (codepoints), not code units. (Example: jsfiddle.net/03tLpbrv) (Also worth noting that JavaScript strings were never ASCII-centric. They were UTF16-centric [with invalid surrogate pairs tolerance].)

If there were a .map added, it would presumably visit characters, not code units, as well; it would be the only reasonable thing to do, both in light of how strings iterate and just general utility/appropriateness.

-- T.J. Crowder

# T.J. Crowder (6 years ago)

On Thu, May 17, 2018 at 4:16 PM, Ben Fletcher <bfletch at gmail.com> wrote:

Consider adding the array .map() implementation directly to the String

prototype.

Here is a possible polyfill:

String.prototype.map = function (fn) {
  return Array.prototype.map
    .call(this, fn)
    .join('');

Recommend something using the string's iterator instead, since the version above will visit code units, not codepoints (characters). Crude unoptimized version (jsfiddle.net/dkty51ys):

Object.defineProperty(String.prototype, "map", {
    value: function(fn, thisArg = undefined) {
        let result = "";
        for (const ch of this) {
            result += fn.call(thisArg, ch);
        }
        return result;
    }
});

or via conversion to array and back (jsfiddle.net/r0uqgx65):

Object.defineProperty(String.prototype, "map", {
    value: function(fn, thisArg = undefined) {
        return [...this].map(fn, thisArg).join("");
    }
});

This replaces the common pattern of splitting, mapping, joining strings for processing.

Is it really that common? If so, the footgun available (iterating code units rather than codepoints), could be an argument in favor...

-- T.J. Crowder

# T.J. Crowder (6 years ago)

Combining marks will continue to be an issue (as they are with iteration), but I'd expect that code dealing with languages for which combining marks are a significant thing wouldn't be doing this sort of mapping in the first place, or will have language-specific rules for handling mapping with combining marks. If it's a pain point for someone, they can always propose a locale-specific mapping function standard, perhaps for ECMA-402...

-- T.J. Crowder

# Scott Sauyet (6 years ago)

On Thu, May 17, 2018 at 11:16 AM, Ben Fletcher <bfletch at gmail.com> wrote:

Consider adding the array .map() implementation directly to the String prototype.

There is a big difference between mapping over an array and mapping over a string. Arrays can hold anything. Strings hold only characters. So for a function that takes something of type A and returns something of type B, mapping it over an array takes an array of As and returns an array of Bs.

['a', 'b', 'c'].map(c => c.charCodeAt(0)) //=> [97, 98, 99]

But since a String only holds characters, we can't make it properly hold the results of c => c.charCodeAt(0):

'abc'.map(c => c.charCodeAt(0)) //=> "979899"

reduce would be a bit different, as it can accumulate to any type.

I think map would be a bad idea here.

# kdex (6 years ago)

Yeah; I am aware that ES2015 added measures that make it possible iterate over codepoints, but that was not quite my point. The problem that I see is that in some scenarios it makes sense to think of a string as an array of bytes, and sometimes you need to iterate over a string in terms of its codepoints. Some might even want to iterate in terms of visible glyphs, taking combining marks into account.

This ambiguity makes String.prototype.map moot, as it remains questionable what exactly should be iterated. Bytes? Codepoints? Entire glyphs?

# Jordan Harband (6 years ago)

What's the use case where you'd want to map over every (character / code point / grapheme / whatever) in a string, and apply the same callback logic to each one?

# Braden Anderson (6 years ago)

We do this on our front end where we replace certain emojis with our custom brand icons.

I don’t support this suggestion, though.

# Andrea Giammarchi (6 years ago)

I agree map is not really a good fit for strings but this looks like yet another use case for :: operator

const map = [].map;

str::map(codeUnits);
[...str]::map(codePoints);

Why didn't that proposal made it again? It would make every request to add prototypal methods redundant!

# Jordan Harband (6 years ago)

We do this on our front end where we replace certain emojis with our

custom brand icons.

Seems like something .replace can already do for you?

# Isiah Meadows (6 years ago)

And to my understanding, .replace can be better optimized for that use case - it can limit graph copying when it builds the new string, among other things.

If they actually need something like the .map as proposed here, they could just as easily do str.replace(/./gsu, m => ...), or if they're

stuck with non-transpiled ES2015, str.replace(/[^]/gu, m => ...).

Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com