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 <[email protected]> On Behalf Of Andrea Giammarchi Sent: Wednesday, October 10, 2018 9:30 PM To: Jordan Harband <[email protected]> Cc: [email protected] 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 <[email protected]<mailto:[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]<mailto:[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]<mailto:[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]<mailto:[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<https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fapi.dartlang.org%2Fstable%2F2.0.0%2Fdart-core%2FMap%2FputIfAbsent.html&data=02%7C01%7Cron.buckton%40microsoft.com%7C80d5a89a1ce449e3fac508d62f32324e%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636748289999447872&sdata=VsBS5LsqzgSF%2BFylqsGDM4uCqUj%2FG%2BYqtbuFLPhEaKU%3D&reserved=0>) - [Java] [Map.putIfAbsent](https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#putIfAbsent-K-V-<https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.oracle.com%2Fjavase%2F8%2Fdocs%2Fapi%2Fjava%2Futil%2FMap.html%23putIfAbsent-K-V-&data=02%7C01%7Cron.buckton%40microsoft.com%7C80d5a89a1ce449e3fac508d62f32324e%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636748289999447872&sdata=gkh4l7LiTCJ0L8%2BGYbVsxlhvmlQYwmK9ZGxkTdhyM1A%3D&reserved=0>) _______________________________________________ es-discuss mailing list [email protected]<mailto:[email protected]> https://mail.mozilla.org/listinfo/es-discuss<https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.mozilla.org%2Flistinfo%2Fes-discuss&data=02%7C01%7Cron.buckton%40microsoft.com%7C80d5a89a1ce449e3fac508d62f32324e%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636748289999447872&sdata=AT2igJX94CnydPQ8daMnVFKq3c3nCQe1fwYxfZOqNXU%3D&reserved=0> _______________________________________________ es-discuss mailing list [email protected]<mailto:[email protected]> https://mail.mozilla.org/listinfo/es-discuss<https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.mozilla.org%2Flistinfo%2Fes-discuss&data=02%7C01%7Cron.buckton%40microsoft.com%7C80d5a89a1ce449e3fac508d62f32324e%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636748289999447872&sdata=AT2igJX94CnydPQ8daMnVFKq3c3nCQe1fwYxfZOqNXU%3D&reserved=0> _______________________________________________ es-discuss mailing list [email protected]<mailto:[email protected]> https://mail.mozilla.org/listinfo/es-discuss<https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.mozilla.org%2Flistinfo%2Fes-discuss&data=02%7C01%7Cron.buckton%40microsoft.com%7C80d5a89a1ce449e3fac508d62f32324e%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636748289999447872&sdata=AT2igJX94CnydPQ8daMnVFKq3c3nCQe1fwYxfZOqNXU%3D&reserved=0>
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

