Proposal: Add Map.prototype.putIfAbsent

Isiah Meadows isiahmeadows at gmail.com
Thu Oct 11 04:07:18 UTC 2018


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/20181011/858f780f/attachment.html>


More information about the es-discuss mailing list