www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Mimicking C++'s indexing behavior in D associative arrays

reply "Matt Kline" <matt bitbashing.io> writes:
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
next sibling parent "Matt Kline" <matt bitbashing.io> writes:
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
prev sibling parent reply Rikki Cattermole <alphaglosined gmail.com> writes:
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
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
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
prev sibling parent FG <home fgda.pl> writes:
 // 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]; }
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); }
Feb 18 2015