On Thu, Oct 11, 2018 at 6:29 AM Andrea Giammarchi <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 >>>>> [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

