Hey Kai, youโ€™re oversimplifying. Your solution works for a single Unicode
symbol (corresponding to a single code point) but falls apart as soon as
you need to match multiple symbols of possibly varying length, like in the
`escapeHtml` example.

On Sat, May 19, 2018 at 8:43 AM, kai zhu <[email protected]> wrote:

> again, you backend-engineers are making something more complicated than
> needs be, when simple, throwaway glue-code will suffice.  agree with
> jordan, this feature is a needless cross-cut of String.prototype.replace.
>
> ```
> /*jslint
>     node: true
> */
> 'use strict';
> var dict;
> dict = {
>     '$': '^',
>     '1': '2',
>     '<': '&lt;',
>     '๐ŸŒ': '๐Ÿ‘',
>     '-': '_',
>     ']': '@'
> };
> // output: "test๐Ÿ๐Ÿ‘_^^[22@ &lt;foo>"
> console.log('test๐Ÿ๐ŸŒ-$$[11] <foo>'.replace((/[\S\s]/gu), function
> (character) {
>     return dict.hasOwnProperty(character)
>         ? dict[character]
>         : character;
> }));
> ```
>
> kai zhu
> [email protected]
>
>
>
> On 19 May 2018, at 4:08 PM, Cyril Auburtin <[email protected]>
> wrote:
>
> You can also have a
>
> ```js
> var replacer = replacements => {
>   const re = new RegExp(replacements.map(([k,_,escaped=k]) =>
> escaped).join('|'), 'gu');
>   const replaceMap = new Map(replacements);
>   return s => s.replace(re, w => replaceMap.get(w));
> }
> var replace = replacer([['$', '^', String.raw`\$`], ['1', '2'], ['<',
> '&lt;'], ['๐ŸŒ', '๐Ÿ‘'], ['-', '_'], [']', '@', String.raw`\]`]]);
> replace('test๐Ÿ๐ŸŒ-$$[11] <foo>') // "test๐Ÿ๐Ÿ‘_^^[22@ &lt;foo>"
> ```
> but it's quickly messy to work with escaping
>
> Le sam. 19 mai 2018 ร  08:17, Isiah Meadows <[email protected]> a
> รฉcrit :
>
>> Here's what I'd prefer instead: overload `String.prototype.replace` to
>> take non-callable objects, as sugar for this:
>>
>> ```js
>> const old = Function.call.bind(Function.call, String.prototype.replace)
>> String.prototype.replace = function (regexp, object) {
>>     if (object == null && regexp != null && typeof regexp === "object") {
>>         const re = new RegExp(
>>             Object.keys(regexp)
>>             .map(key => `${old(key, /[\\^$*+?.()|[\]{}]/g, '\\$&')}`)
>>             .join("|")
>>         )
>>         return old(this, re, m => object[m])
>>     } else {
>>         return old(this, regexp, object)
>>     }
>> }
>> ```
>>
>> This would cover about 99% of my use for something like this, with
>> less runtime overhead (that of not needing to check for and
>> potentially match multiple regular expressions at runtime) and better
>> static analyzability (you only need to check it's an object literal or
>> constant frozen object, not that it's argument is the result of the
>> built-in `Map` call). It's exceptionally difficult to optimize for
>> this unless you know everything's a string, but most cases where I had
>> to pass a callback that wasn't super complex looked a lot like this:
>>
>> ```js
>> // What I use:
>> function escapeHTML(str) {
>>     return str.replace(/["'&<>]/g, m => {
>>         switch (m) {
>>         case '"': return "&#34;"
>>         case "'": return "&#39;"
>>         case "&": return "&amp;"
>>         case "<": return "&lt;"
>>         case ">": return "&gt;"
>>         default: throw new TypeError("unreachable")
>>         }
>>     })
>> }
>>
>> // What it could be
>> function escapeHTML(str) {
>>     return str.replace({
>>         '"': "&#34;",
>>         "'": "&#39;",
>>         "&": "&amp;",
>>         "<": "&lt;",
>>         ">": "&gt;",
>>     })
>> }
>> ```
>>
>> And yes, this enables optimizations engines couldn't easily produce
>> otherwise. In this instance, an engine could find that the object is
>> static with only single-character entries, and it could replace the
>> call to a fast-path one that relies on a cheap lookup table instead
>> (Unicode replacement would be similar, except you'd need an extra
>> layer of indirection with astrals to avoid blowing up memory when
>> generating these tables):
>>
>> ```js
>> // Original
>> function escapeHTML(str) {
>>     return str.replace({
>>         '"': "&#34;",
>>         "'": "&#39;",
>>         "&": "&amp;",
>>         "<": "&lt;",
>>         ">": "&gt;",
>>     })
>> }
>>
>> // Not real JS, but think of it as how an engine might implement this. The
>> // implementation of the runtime function `ReplaceWithLookupTable` is
>> omitted
>> // for brevity, but you could imagine how it could be implemented, given
>> the
>> // pseudo-TS signature:
>> //
>> // ```ts
>> // declare function %ReplaceWithLookupTable(
>> //     str: string,
>> //     table: string[]
>> // ): string
>> // ```
>> function escapeHTML(str) {
>>     static {
>>         // A zero-initialized array with 2^16 entries (U+0000-U+FFFF),
>> except
>>         // for the object's members. This takes up to about 70K per
>> instance,
>>         // but these are *far* more often called than created.
>>         const _lookup_escapeHTML = %calloc(65536)
>>
>>         _lookup_escapeHTML[34] = "&#34;"
>>         _lookup_escapeHTML[38] = "&amp;"
>>         _lookup_escapeHTML[39] = "&#39;"
>>         _lookup_escapeHTML[60] = "&gt;"
>>         _lookup_escapeHTML[62] = "&lt;"
>>     }
>>
>>     return %ReplaceWithLookupTable(str, _lookup_escapeHTML)
>> }
>> ```
>>
>> Likewise, similar, but more restrained, optimizations could be
>> performed on objects with multibyte strings, since they can be reduced
>> to a simple search trie. (These can be built in even the general case
>> if the strings are large enough to merit it - small ropes are pretty
>> cheap to create.)
>>
>> For what it's worth, there's precedent here in Ruby, which has support
>> for `Hash`es as `String#gsub` parameters which work similarly.
>>
>> -----
>>
>> Isiah Meadows
>> [email protected]
>> www.isiahmeadows.com
>>
>>
>> On Fri, May 18, 2018 at 1:01 PM, Logan Smyth <[email protected]>
>> wrote:
>> >> It wouldn't necessarily break existing API, since
>> String.prototype.replace
>> >> currently accepts only RegExp or strings.
>> >
>> > Not quite accurate. It accepts anything with a `Symbol.replace`
>> property, or
>> > a string.
>> >
>> > Given that, what you're describing can be implemented as
>> > ```
>> > Map.prototype[Symbol.replace] = function(str) {
>> >   for(const [key, value] of this) {
>> >     str = str.replace(key, value);
>> >   }
>> >   return str;
>> > };
>> > ```
>> >
>> >> I don't know if the ECMAScript spec mandates preserving a particular
>> order
>> >> to a Map's elements.
>> >
>> > It does, so you're good there.
>> >
>> >> Detecting collisions between matching regular expressions or strings.
>> >
>> > I think this would be my primary concern, but no so much ordering as
>> > expectations. Like if you did
>> > ```
>> > "1".replace(new Map([
>> >   ['1', '2'],
>> >   ['2', '3],
>> > ]);
>> > ```
>> > is the result `2` or `3`? `3` seems surprising to me, at least in the
>> > general sense, because there was no `2` in the original input, but it's
>> also
>> > hard to see how you'd spec the behavior to avoid that if general regex
>> > replacement is supported.
>> >
>> > On Fri, May 18, 2018 at 9:47 AM, Alex Vincent <[email protected]>
>> wrote:
>> >>
>> >> Reading [1] in the digests, I think there might actually be an API
>> >> improvement that is doable.
>> >>
>> >> Suppose the String.prototype.replace API allowed passing in a single
>> >> argument, a Map instance where the keys were strings or regular
>> expressions
>> >> and the values were replacement strings or functions.
>> >>
>> >> Advantages:
>> >> * Shorthand - instead of writing str.replace(a, b).replace(c,
>> >> d).replace(e, f)... you get str.replace(regExpMap)
>> >> * Reusable - the same regular expression/string map could be used for
>> >> several strings (assuming of course the user didn't just abstract the
>> call
>> >> into a separate function)
>> >> * Modifiable on demand - developers could easily add new regular
>> >> expression matches to the map object, or remove them
>> >> * It wouldn't necessarily break existing API, since
>> >> String.prototype.replace currently accepts only RegExp or strings.
>> >>
>> >> Disadvantages / reasons not to do it:
>> >> * Detecting collisions between matching regular expressions or strings.
>> >> If two regular expressions match the same string, or a regular
>> expression
>> >> and a search string match, the expected results may vary because a
>> Map's
>> >> elements might not be consistently ordered.  I don't know if the
>> ECMAScript
>> >> spec mandates preserving a particular order to a Map's elements.
>> >>   - if we preserve the same chaining capability
>> >> (str.replace(map1).replace(map2)...), this might not be a big problem.
>> >>
>> >> The question is, how often do people chain replace calls together?
>> >>
>> >> * It's not particularly hard to chain several replace calls together.
>> >> It's just verbose, which might not be a high enough burden to overcome
>> for
>> >> adding API.
>> >>
>> >> That's my two cents for the day.  Thoughts?
>> >>
>> >> [1] https://esdiscuss.org/topic/adding-map-directly-to-string-
>> prototype
>> >>
>> >> --
>> >> "The first step in confirming there is a bug in someone else's work is
>> >> confirming there are no bugs in your own."
>> >> -- Alexander J. Vincent, June 30, 2001
>> >>
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> [email protected]
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> >>
>> >
>> >
>> > _______________________________________________
>> > es-discuss mailing list
>> > [email protected]
>> > https://mail.mozilla.org/listinfo/es-discuss
>> >
>> _______________________________________________
>> es-discuss mailing list
>> [email protected]
>> https://mail.mozilla.org/listinfo/es-discuss
>>
> _______________________________________________
> es-discuss mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/es-discuss
>
>
>
> _______________________________________________
> es-discuss mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/es-discuss
>
>
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to