Proposal: Add Map.prototype.putIfAbsent

Jordan Harband ljharb at gmail.com
Thu Oct 11 04:19:50 UTC 2018


Thanks, your correction explains what the benefit would be (the awkward
need to cache the value). However this seems simpler to me: `if
(!map.has(key)) { map.set(key, getValue()); } const value = map.get(key);`

On Wed, Oct 10, 2018 at 9:07 PM Isiah Meadows <isiahmeadows at gmail.com>
wrote:

> I presume you mean this?
>
> ```js
> // Proposed
> map.putIfAbsent(key, init)
>
> // How you do it now
> let value
> if (map.has(key)) {
>     value = map.get(key)
> } else {
>     map.set(key, value = init())
> }
> ```
>
> BTW, I'd like to see this make it myself, just slightly different:
>
> - How you call it: `map.getOrPut(key, init, thisValue=undefined)`
> - How `init` is called: `init.call(thisValue, key, map)`
>
> This pattern is incredibly common for caching. It'd be nice if I didn't
> have to repeat myself so much with it. I would be more willing to stuff it
> in a utility function if it weren't for the fact the use cases often entail
> performance-sensitive paths, and engines aren't reliable enough in my
> experience with inlining closures passed to non-builtins.
>
> On Wed, Oct 10, 2018 at 22:54 Jordan Harband <ljharb at gmail.com> wrote:
>
>> It seems like your proposed `const value = map.putIfAbsent(key,
>> getExpensiveValue);` is achievable already with `const value = map.has(key)
>> ? map.get(key) : map.set(getExpensiveValue());` - am I understanding your
>> suggestion correctly?
>>
>> On Wed, Oct 10, 2018 at 12:46 AM Man Hoang <jolleekin at outlook.com> wrote:
>>
>>> Consider the following function
>>>
>>> ``` js
>>>
>>> /**
>>>
>>> * Parses the locale sensitive string [value] into a number.
>>>
>>> */
>>>
>>> export function parseNumber(
>>>
>>>     value: string,
>>>
>>>     locale: string = navigator.language
>>>
>>> ): number {
>>>
>>>     let decimalSeparator = decimalSeparators.get(locale);
>>>
>>>     if (!decimalSeparator) {
>>>
>>>         decimalSeparator = Intl.NumberFormat(locale).format(1.1)[1];
>>>
>>>         decimalSeparators.set(locale, decimalSeparator);
>>>
>>>     }
>>>
>>>
>>>
>>>     let cleanRegExp = regExps.get(decimalSeparator);
>>>
>>>     if (!cleanRegExp) {
>>>
>>>         cleanRegExp = new RegExp(`[^-+0-9${decimalSeparator}]`, 'g');
>>>
>>>         regExps.set(decimalSeparator, cleanRegExp);
>>>
>>>     }
>>>
>>>
>>>
>>>     value = value
>>>
>>>         .replace(cleanRegExp, '')
>>>
>>>         .replace(decimalSeparator, '.');
>>>
>>>
>>>
>>>     return parseFloat(value);
>>>
>>> }
>>>
>>>
>>>
>>> const decimalSeparators = new Map<string, string>();
>>>
>>> const regExps = new Map<string, RegExp>();
>>>
>>> ```
>>>
>>>
>>>
>>> This function can be simplified quite a bit as follows
>>>
>>> ``` js
>>>
>>> export function parseNumber(
>>>
>>>     value: string,
>>>
>>>     locale: string = navigator.language
>>>
>>> ): number {
>>>
>>>     const decimalSeparator = decimalSeparators.putIfAbsent(
>>>
>>>         locale, () => Intl.NumberFormat(locale).format(1.1)[1]);
>>>
>>>
>>>
>>>     const cleanRegExp = regExps.putIfAbsent(
>>>
>>>         decimalSeparator, () => new
>>> RegExp(`[^-+0-9${decimalSeparator}]`, 'g'));
>>>
>>>
>>>
>>>     value = value
>>>
>>>         .replace(cleanRegExp, '')
>>>
>>>         .replace(decimalSeparator, '.');
>>>
>>>
>>>
>>>     return parseFloat(value);
>>>
>>> }
>>>
>>> ```
>>>
>>> if `Map` has the following instance method
>>>
>>> ``` js
>>>
>>> export class Map<K, V> {
>>>
>>>     /**
>>>
>>>      * Look up the value of [key], or add a new value if it isn't there.
>>>
>>>      *
>>>
>>>      * Returns the value associated to [key], if there is one.
>>>
>>>      * Otherwise calls [ifAbsent] to get a new value, associates [key] to
>>>
>>>      * that value, and then returns the new value.
>>>
>>>      */
>>>
>>>     putIfAbsent(key: K, ifAbsent: () => V): V {
>>>
>>>         let v = this.get(key);
>>>
>>>         if (v === undefined) {
>>>
>>>             v = ifAbsent();
>>>
>>>             this.set(key, v);
>>>
>>>         }
>>>
>>>         return v;
>>>
>>>     }
>>>
>>> }
>>>
>>> ```
>>>
>>>
>>>
>>> Java's Map has a `putIfAbsent` method, which accepts a value rather than
>>> a function for the second parameter. This is not ideal as computing the
>>> value may be expensive. A function would allow the value to be computed
>>> lazily.
>>>
>>>
>>>
>>> References
>>>
>>> - [Dart] [Map.putIfAbsent](
>>> https://api.dartlang.org/stable/2.0.0/dart-core/Map/putIfAbsent.html)
>>>
>>> - [Java] [Map.putIfAbsent](
>>> https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#putIfAbsent-K-V-
>>> )
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20181010/f51e2196/attachment-0001.html>


More information about the es-discuss mailing list