Proposal: Add Map.prototype.putIfAbsent

classic Classic list List threaded Threaded
16 messages Options
Reply | Threaded
Open this post in threaded view
|

Proposal: Add Map.prototype.putIfAbsent

Man Hoang

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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Jordan Harband
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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Isiah Meadows-2
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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Jordan Harband
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Andrea Giammarchi-2
No. The raised concern has been common among developers and the main issue is that `set` returns itself which is the least useful pattern.

Your suggestion would make sense if `const value = map.has(key) ? map.get(key) : map.set(key, createValue())` instead ES went for chain ability and yet, to date, I have to see a single usage of `map.set(a, 1).set(b, 2)` in the wild.

On top of that, needing to `.has` and then `.get` is a performance shenanigan many would likely avoid at all costs 'cause AFAIK in no engine `.has(key)` temporarily retains last searched key to boost up the immediate `.get(key)` later on so having a `.putIfAbsent(key, createValue())` that returns either the found or the created value is a clear win.



On Thu, Oct 11, 2018 at 6:20 AM Jordan Harband <[hidden email]> wrote:
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Isiah Meadows-2
In reply to this post by Jordan Harband
It's slghtly simpler in terms of lines of code, but it's still more awkward than it should be for that kind of thing.

On Thu, Oct 11, 2018 at 00:20 Jordan Harband <[hidden email]> wrote:
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: Proposal: Add Map.prototype.putIfAbsent

Ron Buckton
In reply to this post by Andrea Giammarchi-2

I have seen this in other languages as `getOrCreate(key, valueFactory)`. Dictionaries in .NET have a `TryGetValue(key, out value)` method that returns a boolean and has an ‘out’ parameter used to assign the value if it exists. The performance issue regarding keys is one of my common concerns with `Set.prototype.add`, as it would have been significantly more useful for `add` to return a Boolean indicating whether the value was added (true), or was already present (false).

 

At the end of the day, I usually just end up defining `mapGetOrCreate(map, key, valueFactory)` and `setAdd(set, value)` utility functions that I end up using.

 

From: es-discuss <[hidden email]> On Behalf Of Andrea Giammarchi
Sent: Wednesday, October 10, 2018 9:30 PM
To: Jordan Harband <[hidden email]>
Cc: [hidden email]
Subject: Re: Proposal: Add Map.prototype.putIfAbsent

 

No. The raised concern has been common among developers and the main issue is that `set` returns itself which is the least useful pattern.

 

Your suggestion would make sense if `const value = map.has(key) ? map.get(key) : map.set(key, createValue())` instead ES went for chain ability and yet, to date, I have to see a single usage of `map.set(a, 1).set(b, 2)` in the wild.

 

On top of that, needing to `.has` and then `.get` is a performance shenanigan many would likely avoid at all costs 'cause AFAIK in no engine `.has(key)` temporarily retains last searched key to boost up the immediate `.get(key)` later on so having a `.putIfAbsent(key, createValue())` that returns either the found or the created value is a clear win.

 

 

 

On Thu, Oct 11, 2018 at 6:20 AM Jordan Harband <[hidden email]> wrote:

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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Jack Lu
In reply to this post by Man Hoang
Don't you think the name `putIfAbsent` is somewhat misleading? In the sense that the purpose of this function is to get the value, with a side effect of setting a default value when it's absent.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Andrea Giammarchi-2
I think it boils down to the fact `.put(value)` is a well known method outside the JS world that returns the value.

I honestly would **love** to have `.put` in Map, Set, and Weak friends, so that the whole thing would be:

```js
const value = map.has(key) ? map.get(key) : map.put(key, createValue());

// with the more than common safe shortcut as
const value = map.get(key) || map.put(key, createValue());
```

If you understand that `put` by default returns the value you are putting, `putIfAbsent` makes perfect sense as a method that `put` the value only if absent but like `put` would return either the present one, or the one stored 'cause absent.




On Thu, Oct 11, 2018 at 6:19 PM Jack Lu <[hidden email]> wrote:
Don't you think the name `putIfAbsent` is somewhat misleading? In the sense that the purpose of this function is to get the value, with a side effect of setting a default value when it's absent._______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re Proposal: Add Map.prototype.putIfAbsent

Jack Lu
It makes sense for a `put` to return the value, but it's awkward to say `put` when you want to get a value.
And I'm fond of your proposal of `put`.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re Proposal: Add Map.prototype.putIfAbsent

Andrea Giammarchi-2
I think having both `set` and `put` would be actually awesome, we have `substr` and `slice` as well that do slightly different things with slightly different names and there are valid use cases/simplification for both string methods.

AFAIK Allen mentioned it'd be awkward to have put too, but I hope others agree it would solve most issues developers have with constants and one/off WeakMap setups which is the most common use case with weak maps, weak sets, and not weak counterparts.

Regards

On Fri, Oct 12, 2018 at 3:57 AM Jack Lu <[hidden email]> wrote:
It makes sense for a `put` to return the value, but it's awkward to say `put` when you want to get a value.
And I'm fond of your proposal of `put`.

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re Proposal: Add Map.prototype.putIfAbsent

Peter Hoddie
we have `substr` and `slice` as well that do slightly different things with slightly different names and there are valid use cases/simplification for both string methods

substr is in Annex B for web compatibility. 

The specification notes:

All of the language features and behaviours specified in this annex have one or more undesirable characteristics and in the absence of legacy usage would be removed from this specification

And:

These features are not considered part of the core ECMAScript language. Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code. ECMAScript implementations are discouraged from implementing these features unless the implementation is part of a web browser or is required to run the same legacy ECMAScript code that web browsers encounter.

Our XS engine for emebdded followed this guidance: we didn't implement strstr for a long time. Alas, developers from the web are so accustomed to having it that we eventually gave in and provided it. Some conformance details here:


-- Peter

On Oct 12, 2018, at 9:54 AM, Andrea Giammarchi <[hidden email]> wrote:

I think having both `set` and `put` would be actually awesome, we have `substr` and `slice` as well that do slightly different things with slightly different names and there are valid use cases/simplification for both string methods.

AFAIK Allen mentioned it'd be awkward to have put too, but I hope others agree it would solve most issues developers have with constants and one/off WeakMap setups which is the most common use case with weak maps, weak sets, and not weak counterparts.

Regards

On Fri, Oct 12, 2018 at 3:57 AM Jack Lu <[hidden email]> wrote:
It makes sense for a `put` to return the value, but it's awkward to say `put` when you want to get a value.
And I'm fond of your proposal of `put`.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re Proposal: Add Map.prototype.putIfAbsent

Isiah Meadows-2
I personally wish that'd be moved into the core standard simply
because it's easier in a lot of cases (when you're doing offset +
length instead of start/end). But I'm not really that opinionated over
it.

-----

Isiah Meadows
[hidden email]
www.isiahmeadows.com

On Fri, Oct 12, 2018 at 2:05 PM Peter Hoddie <[hidden email]> wrote:

>
> we have `substr` and `slice` as well that do slightly different things with slightly different names and there are valid use cases/simplification for both string methods
>
>
> substr is in Annex B for web compatibility.
>
> The specification notes:
>
> All of the language features and behaviours specified in this annex have one or more undesirable characteristics and in the absence of legacy usage would be removed from this specification
>
> And:
>
> These features are not considered part of the core ECMAScript language. Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code. ECMAScript implementations are discouraged from implementing these features unless the implementation is part of a web browser or is required to run the same legacy ECMAScript code that web browsers encounter.
>
> Our XS engine for emebdded followed this guidance: we didn't implement strstr for a long time. Alas, developers from the web are so accustomed to having it that we eventually gave in and provided it. Some conformance details here:
>
> https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/xs/XS%20Differences.md#conformance
>
> -- Peter
>
> On Oct 12, 2018, at 9:54 AM, Andrea Giammarchi <[hidden email]> wrote:
>
> I think having both `set` and `put` would be actually awesome, we have `substr` and `slice` as well that do slightly different things with slightly different names and there are valid use cases/simplification for both string methods.
>
> AFAIK Allen mentioned it'd be awkward to have put too, but I hope others agree it would solve most issues developers have with constants and one/off WeakMap setups which is the most common use case with weak maps, weak sets, and not weak counterparts.
>
> Regards
>
> On Fri, Oct 12, 2018 at 3:57 AM Jack Lu <[hidden email]> wrote:
>>
>> It makes sense for a `put` to return the value, but it's awkward to say `put` when you want to get a value.
>> And I'm fond of your proposal of `put`.
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
>
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re Proposal: Add Map.prototype.putIfAbsent

Andrea Giammarchi-2
In reply to this post by Andrea Giammarchi-2
You slightly missed my point Peter ... put and set are completely different and it wouldn't be awkward to have them both, neither I'm suggesting to put in annex b set or put: both have use cases, both are valid, both add values, both are different.

On Fri, Oct 12, 2018 at 7:43 PM Peter Hoddie <[hidden email]> wrote:
we have `substr` and `slice` as well that do slightly different things with slightly different names and there are valid use cases/simplification for both string methods

substr is in Annex B for web compatibility. 

The specification notes:

All of the language features and behaviours specified in this annex have one or more undesirable characteristics and in the absence of legacy usage would be removed from this specification

And:

These features are not considered part of the core ECMAScript language. Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code. ECMAScript implementations are discouraged from implementing these features unless the implementation is part of a web browser or is required to run the same legacy ECMAScript code that web browsers encounter.

Our XS engine for emebdded followed this guidance: we didn't implement strstr for a long time. Alas, developers from the web are so accustomed to having it that we eventually gave in and provided it. Some conformance details here:


-- Peter

On Oct 12, 2018, at 9:54 AM, Andrea Giammarchi <[hidden email]> wrote:

I think having both `set` and `put` would be actually awesome, we have `substr` and `slice` as well that do slightly different things with slightly different names and there are valid use cases/simplification for both string methods.

AFAIK Allen mentioned it'd be awkward to have put too, but I hope others agree it would solve most issues developers have with constants and one/off WeakMap setups which is the most common use case with weak maps, weak sets, and not weak counterparts.

Regards

On Fri, Oct 12, 2018 at 3:57 AM Jack Lu <[hidden email]> wrote:
It makes sense for a `put` to return the value, but it's awkward to say `put` when you want to get a value.
And I'm fond of your proposal of `put`.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Sathya Gunasekaran
In reply to this post by Andrea Giammarchi-2
On Thu, Oct 11, 2018 at 6:29 AM Andrea Giammarchi
<[hidden email]> wrote:
>
> No. The raised concern has been common among developers and the main issue is that `set` returns itself which is the least useful pattern.
>
> Your suggestion would make sense if `const value = map.has(key) ? map.get(key) : map.set(key, createValue())` instead ES went for chain ability and yet, to date, I have to see a single usage of `map.set(a, 1).set(b, 2)` in the wild.
>
> On top of that, needing to `.has` and then `.get` is a performance shenanigan many would likely avoid at all costs 'cause AFAIK in no engine `.has(key)` temporarily retains last searched key to boost up the immediate `.get(key)` later on so having a `.putIfAbsent(key, createValue())` that returns either the found or the created value is a clear win.
>

V8 implemented this[0] and then reverted it[1] because it didn't
actually help with performance. IIRC, JSC still has this optimization.

[0]: https://chromium.googlesource.com/v8/v8/+/3ca6408511900328e11175c38b128b3a6cebb7ec
[1]: https://chromium.googlesource.com/v8/v8/+/a27daf04a36ead5484e7a09a447af918555006dc

>
>
> On Thu, Oct 11, 2018 at 6:20 AM Jordan Harband <[hidden email]> wrote:
>>
>> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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
>>>>> [hidden email]
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> [hidden email]
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Add Map.prototype.putIfAbsent

Andrea Giammarchi-2
For what I could test, `map.has(any)` followed by `map.get(any)` is already very performant in both Chrome and Safari but having `mep.set(any, value)` returning `map` instead of `value` is still annoying for most common use cases.

On Mon, Oct 15, 2018 at 2:49 PM Sathya Gunasekaran <[hidden email]> wrote:
On Thu, Oct 11, 2018 at 6:29 AM Andrea Giammarchi
<[hidden email]> wrote:
>
> No. The raised concern has been common among developers and the main issue is that `set` returns itself which is the least useful pattern.
>
> Your suggestion would make sense if `const value = map.has(key) ? map.get(key) : map.set(key, createValue())` instead ES went for chain ability and yet, to date, I have to see a single usage of `map.set(a, 1).set(b, 2)` in the wild.
>
> On top of that, needing to `.has` and then `.get` is a performance shenanigan many would likely avoid at all costs 'cause AFAIK in no engine `.has(key)` temporarily retains last searched key to boost up the immediate `.get(key)` later on so having a `.putIfAbsent(key, createValue())` that returns either the found or the created value is a clear win.
>

V8 implemented this[0] and then reverted it[1] because it didn't
actually help with performance. IIRC, JSC still has this optimization.

[0]: https://chromium.googlesource.com/v8/v8/+/3ca6408511900328e11175c38b128b3a6cebb7ec
[1]: https://chromium.googlesource.com/v8/v8/+/a27daf04a36ead5484e7a09a447af918555006dc

>
>
> On Thu, Oct 11, 2018 at 6:20 AM Jordan Harband <[hidden email]> wrote:
>>
>> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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
>>>>> [hidden email]
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> [hidden email]
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss