digitalmars.D.learn - Mimicking C++'s indexing behavior in D associative arrays
- Matt Kline (17/17) Feb 17 2015 In C++, the index operator for maps will either return a
- Matt Kline (4/5) Feb 17 2015 should of course be
- Rikki Cattermole (16/31) Feb 17 2015 So basically:
- bearophile (6/8) Feb 18 2015 Is this the setdefault of Python dicts? If this need is strong a
- FG (21/39) Feb 18 2015 You are searching for the key twice and the original example used pointe...
In C++, the index operator for maps will either return a reference to the existing value if the key can be found, or a reference to a new, default-initialized value if one with the given key cannot be found. In D, an exception is thrown instead when a value with the given key cannot be found, similar to unordered_map::at in C++. So if I want to mimic the same behavior (get or initialize to default), I have to do something like // Assume bar is some associative array of type Foo[string] Foo* value = key in bar; if (value) { bar[key] = Foo.init; value = &bar[key]; } This seems sub-optimal, given that in involves three hashes (two lookups and one insertion). Is there a more efficient or cleaner way to do so?
Feb 17 2015
On Wednesday, 18 February 2015 at 00:21:11 UTC, Matt Kline wrote:if (value) {should of course be if (!value) { Sorry for the typo.
Feb 17 2015
On 18/02/2015 1:21 p.m., Matt Kline wrote:In C++, the index operator for maps will either return a reference to the existing value if the key can be found, or a reference to a new, default-initialized value if one with the given key cannot be found. In D, an exception is thrown instead when a value with the given key cannot be found, similar to unordered_map::at in C++. So if I want to mimic the same behavior (get or initialize to default), I have to do something like // Assume bar is some associative array of type Foo[string] Foo* value = key in bar; if (value) { bar[key] = Foo.init; value = &bar[key]; } This seems sub-optimal, given that in involves three hashes (two lookups and one insertion). Is there a more efficient or cleaner way to do so?So basically: Foo v = bar.get(key, Foo.init) bar[key] = v; Get is like an ordinary index but it will return the given value if it does not exist in the AA. Of course you probably want to create a new UFCS function to wrap your check + default initialize if it doesn't exist. T grab(T, U)(T[U] aa, U key) if (is(T == struct)) { if (key !in aa) aa[key] = new T; return aa[key]; } So: Foo*[string] bar; Foo v = *bar.grab("mykey");
Feb 17 2015
Rikki Cattermole:Foo*[string] bar; Foo v = *bar.grab("mykey");Is this the setdefault of Python dicts? If this need is strong a new function could be added to Phobos (or even druntime if you want to reduce the number of hash computations). Bye, bearophile
Feb 18 2015
You are searching for the key twice and the original example used pointers. There is a function called _aaGetX in the runtime that has exactly the required behaviour: // Get pointer to value in associative array indexed by key. // Add entry for key if it is not already there. void* _aaGetX(AA* aa, const TypeInfo keyti, in size_t valuesize, in void* pkey) however, using it in normal code could be considered a hack, because it belongs to the internal implementation of associative arrays. Anyway, while waiting for a better solution to present itself, we might as well have a look at this very dirty one. ;) extern(C) void* _aaGetX(void* aa, const TypeInfo keyti, in size_t valuesize, in void* pkey); V* aaGet(K, V)(V[K] arr, K key) { return cast(V*)_aaGetX(cast(void*)&arr, typeid(K), V.sizeof, cast(void*)&key); } unittest { int[int] arr = [1: 10, 2: 20, 3: 30]; int *val = arr.aaGet(3); // an existing value assert(*val == 30); val = arr.aaGet(4); // aa[4] will be created assert(*val == int.init); assert(arr[4] == int.init); }// Assume bar is some associative array of type Foo[string] Foo* value = key in bar; if (!value) { bar[key] = Foo.init; value = &bar[key]; } This seems sub-optimal, given that in involves three hashes (two lookups and one insertion). Is there a more efficient or cleaner way to do so?So basically: Foo v = bar.get(key, Foo.init) bar[key] = v; Get is like an ordinary index but it will return the given value if it does not exist in the AA. Of course you probably want to create a new UFCS function to wrap your check + default initialize if it doesn't exist. T grab(T, U)(T[U] aa, U key) if (is(T == struct)) { if (key !in aa) aa[key] = new T; return aa[key]; }
Feb 18 2015