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 <[email protected]> wrote: > 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

