www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Use .get() in MultiD Assoc Array?

reply "Paul" <phshaffer gmail.com> writes:
 From the book a way to respond to a non-existent key in an assoc. 
array:

assert(aa["hello"] == "ciao");
// Key "hello" exists, therefore ignore the second argume
assert(aa.get("hello", "salute") == "ciao");
// Key "yo" doesn’t exist, return the second argument
assert(aa.get("yo", "buongiorno") == "buongiorno");

Should this work in multidimensional arrays?

aa.get("key1" "key2" "key2", "nonexistent") == "sometext"

Thanks for your time.
Aug 30 2012
next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Thu, Aug 30, 2012 at 7:18 PM, Paul <phshaffer gmail.com> wrote:
 From the book a way to respond to a non-existent key in an assoc. array:

 assert(aa["hello"] =3D=3D "ciao");
 // Key "hello" exists, therefore ignore the second argume
 assert(aa.get("hello", "salute") =3D=3D "ciao");
 // Key "yo" doesn=E2=80=99t exist, return the second argument
 assert(aa.get("yo", "buongiorno") =3D=3D "buongiorno");

 Should this work in multidimensional arrays?

 aa.get("key1" "key2" "key2", "nonexistent") =3D=3D "sometext"

D multi-key associative arrays do not exist as such, they are associative arrays inside one another. When you write int[string][string][double] aa; you can use get() on aa, but only on its keys, which are of type double, whereas its values are of type int[string][string]. I guess an effect similar to what you're asking can be obtained by using a tuple as a key: import std.typecons; int[Tuple!(string,string,double)] aa; auto p =3D aa.get(tuple("abc","def", 3.14), 0); // 0 is the default value.
Aug 30 2012
prev sibling next sibling parent reply "Paul" <phshaffer gmail.com> writes:
On Thursday, 30 August 2012 at 18:20:02 UTC, Philippe Sigaud 
wrote:
 On Thu, Aug 30, 2012 at 7:18 PM, Paul <phshaffer gmail.com> 
 wrote:
 From the book a way to respond to a non-existent key in an 
 assoc. array:

 assert(aa["hello"] == "ciao");
 // Key "hello" exists, therefore ignore the second argume
 assert(aa.get("hello", "salute") == "ciao");
 // Key "yo" doesn’t exist, return the second argument
 assert(aa.get("yo", "buongiorno") == "buongiorno");

 Should this work in multidimensional arrays?

 aa.get("key1" "key2" "key2", "nonexistent") == "sometext"

D multi-key associative arrays do not exist as such, they are associative arrays inside one another. When you write int[string][string][double] aa; you can use get() on aa, but only on its keys, which are of type double, whereas its values are of type int[string][string]. I guess an effect similar to what you're asking can be obtained by using a tuple as a key: import std.typecons; int[Tuple!(string,string,double)] aa; auto p = aa.get(tuple("abc","def", 3.14), 0); // 0 is the default value.

my array is of the form string[string][string][string] abc;
Aug 30 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/31/2012 08:56 AM, Paul wrote:
 You're welcome. Note that your need of having a structure which is
 both associative and ordered is, if not unheard-of, at least somewhat
 uncommon.

I'm parsing program blocks from a proprietary HW/SW system. They provide the data in the form of: Somegroupname/Someblockname someparam=value anotherparam=value ... otherparam=value end Somegroupname/Somediffblockname someparam=value anotherparam=value ... otherparam=value end Someothergroupname/Someotherblockname p1=value p2=value ... px=value end The data is in an ascii text file. I need to be able to search it by group/block/parameter. I need to be able to maintain group/block order. There are ~hundred diff block types where the params and order of params are known...though I would rather not create all of these structures or lists ahead of time. My greatest need at this point is to compare two files block by block. The blocks may be in diff orders between the files but the params of each block type would always be the same in the same order. So compare groups, blocks within groups, and the values of each param for matching group/block names.

Wrap your string[string][string][string] in a user-defined type that provides "the 'in' operator" as well as opIndex and friends. I have started writing this but could not finish it yet: import std.exception; class MyTable { string[string][string][string] elements; struct Index { string i0; string i1; string i2; } // Enables the 'auto element = myIndex in myTable' syntax // (Note: I should have called it Key, not Index.) string * opBinary(string op)(Index index) if (op == "in") { string * result = null; if (auto table0 = index.i0 in elements) { if (auto table1 = index.i1 in *table0) { if (auto element = index.i2 in *table1) { result = element; } } } return result; } // Enables 'auto value = myTable[myIndex]' ref string opIndex(Index index) { string * result = this.opBinary!"in"(index); enforce(result); return *result; } // Enables 'myTable[myIndex] = value' void opIndexAssign(string value, Index index) { auto existing = this.opIndex(index); if (existing) { existing = value; } else { // TODO: ensure that this Index exists } } // TODO: Look up oopIndexUnary and opIndexOpAssign string get(Index index, string defaultValue) { string * result = this.opBinary!"in"(index); return result ? *result : defaultValue; } } void main() {} Ali
Aug 31 2012
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/31/2012 11:55 AM, Ali Çehreli wrote:

 class MyTable

[...]
 // Enables the 'auto element = myIndex in myTable' syntax

That's wrong. For that syntax to work, the operator below should have been opBinaryRight.
 string * opBinary(string op)(Index index)

Yeah, that should have been opBinaryRight. (And the badly designed Thunderbird removes the indentation in quoted text. Smart application or stupid designer?)
 // Enables 'auto value = myTable[myIndex]'
 ref string opIndex(Index index)
 {
 string * result = this.opBinary!"in"(index);

If I had defined opBinaryRight as I should have, then I could simply use the 'in' operator on the right-hand side: string * result = index in this;
 Ali

Ali "too"
Aug 31 2012
prev sibling next sibling parent "Paul" <phshaffer gmail.com> writes:
On Thursday, 30 August 2012 at 18:29:28 UTC, Paul wrote:
 On Thursday, 30 August 2012 at 18:20:02 UTC, Philippe Sigaud 
 wrote:
 On Thu, Aug 30, 2012 at 7:18 PM, Paul <phshaffer gmail.com> 
 wrote:
 From the book a way to respond to a non-existent key in an 
 assoc. array:

 assert(aa["hello"] == "ciao");
 // Key "hello" exists, therefore ignore the second argume
 assert(aa.get("hello", "salute") == "ciao");
 // Key "yo" doesn’t exist, return the second argument
 assert(aa.get("yo", "buongiorno") == "buongiorno");

 Should this work in multidimensional arrays?

 aa.get("key1" "key2" "key2", "nonexistent") == "sometext"

D multi-key associative arrays do not exist as such, they are associative arrays inside one another. When you write int[string][string][double] aa; you can use get() on aa, but only on its keys, which are of type double, whereas its values are of type int[string][string]. I guess an effect similar to what you're asking can be obtained by using a tuple as a key: import std.typecons; int[Tuple!(string,string,double)] aa; auto p = aa.get(tuple("abc","def", 3.14), 0); // 0 is the default value.

my array is of the form string[string][string][string] abc;

Maybe I'm not going about my project from the best angle? Another problem I have is when I go to printout my array, being associative, it is not in the order I built it. It would help greatly if I could print it in order. Maybe I should look into this "tuple" thing more. As well I would like to be able to check for the existence of a particular "key" quickly without setting up one those three-tier foreach iteration loops.
Aug 30 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, August 30, 2012 20:57:44 Paul wrote:
 Maybe I'm not going about my project from the best angle?
 Another problem I have is when I go to printout my array, being
 associative, it is not in the order I built it. It would help
 greatly if I could print it in order.

Hash tables (and an associative array is a hash table) are unordered, so you're not going to get them out in the order that you put them in or anything like that. If you use std.container.RedBlackTree (which defaults to being a set but can be used as a map if you adjust its comparison predicate appropriately), then you have ordering, but it's ordered according to its predicate, not the insertion order. I believe that if you want a map (be it ordered or unordered) to give you items back in the order that you inserted them, then a separate list is required (be it integrated into the container or something you do alongside it) where that list has the keys in the order that they were inserted into the container. - Jonathan M Davis
Aug 30 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Thu, Aug 30, 2012 at 8:57 PM, Paul <phshaffer gmail.com> wrote:

 Maybe I'm not going about my project from the best angle?  Another problem I
 have is when I go to printout my array, being associative, it is not in the
 order I built it.  It would help greatly if I could print it in order.

Associative arrays reorganize themselves to allow for fast insertion/retrieval. They do not make any promise concerning order or printing.
 Maybe I should look into this "tuple" thing more.  As well I would like to
 be able to check for the existence of a particular "key" quickly without
 setting up one those three-tier foreach iteration loops.

A tuple is just a grouping of values together, its just a handy predefined struct-maker in std.typecons. In your case, its equivalent to defining struct MyKey { string a,b,c} string[MyKey] aa; to fill it: aa[MyKey("abc","def","ghi")] = "my value"; It depends whether you need intermediate associative arrays (aa["abc"], aa["abc"]["def"]) or not. It's like a 2D/3D array: do you want access to lines / arrays or just to individual elements. In your case, if you just want access to individual elements, using a key grouping all your key strings under one struct might be interesting: it means just one query in the AA. To check for a key: auto wanted = MyKey("abc", "def", "ghi"); if (auto p = wanted in aa) // p !is null, => the key is in the associative array { // use *p }
Aug 30 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Thu, Aug 30, 2012 at 9:30 PM, Jonathan M Davis <jmdavisProg gmx.com> wrote:

 I believe that if you want a map (be it ordered or unordered) to give you
 items back in the order that you inserted them, then a separate list is
 required (be it integrated into the container or something you do alongside
 it) where that list has the keys in the order that they were inserted into the
 container.

Yes, that's what I wanted to propose. Group an AA and a standard, dynamic, array. The array is just filled with the key, when you assign a new key/value pair. To query the structure, use the AA. To print the structure in order, iterate on this array of keys and query the AA accordingly. Of course, there is a downside: * when you discard a key/value pair, your key array must be iterated to find the now-discarded key and recreated after the key. * when you re-assign an already defined key, you might want to put the key in front/back of the array once more. Pro: in order printing/iteration. Con: slow discarding, slow re-assigning.
Aug 30 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, August 30, 2012 21:40:34 Philippe Sigaud wrote:
 On Thu, Aug 30, 2012 at 9:30 PM, Jonathan M Davis <jmdavisProg gmx.com> 

 I believe that if you want a map (be it ordered or unordered) to give you
 items back in the order that you inserted them, then a separate list is
 required (be it integrated into the container or something you do
 alongside
 it) where that list has the keys in the order that they were inserted into
 the container.

Yes, that's what I wanted to propose. Group an AA and a standard, dynamic, array. The array is just filled with the key, when you assign a new key/value pair. To query the structure, use the AA. To print the structure in order, iterate on this array of keys and query the AA accordingly. Of course, there is a downside: * when you discard a key/value pair, your key array must be iterated to find the now-discarded key and recreated after the key. * when you re-assign an already defined key, you might want to put the key in front/back of the array once more. Pro: in order printing/iteration. Con: slow discarding, slow re-assigning.

Yeah, though I expect that there are ways to make it a less of a problem. For instance, you could make it so that the hash table's value was really a tuple of the value and the index into the array or vector holding the keys in insertion order (or use a second hash table to hold the indices). Then when you remove an item, you use the index from the value in the table and set that element in the array to a value indicating that it's empty (so that it's skipped when iterating over it). Then removing items is as fast as inserting them. The problem that that causes of course is that the list of inserted keys keeps growing, but as long as it doesn't make rehashing too expensive, you could adjust the array (and the indices in the hash table) when you rehash (and possibly after a certain number of removals as well in case the table gets items removed often enough that rehashing isn't ever necessary). That may or may not be the best solution, but I expect that it's problem that's already been explored and probably has some good existing solutions somewhere. In general though, you just don't care about insertion order into a hash table, which takes care of the whole problem. - Jonathan M Davis
Aug 30 2012
prev sibling next sibling parent "Paul" <phshaffer gmail.com> writes:
On Thursday, 30 August 2012 at 19:40:44 UTC, Philippe Sigaud 
wrote:
 On Thu, Aug 30, 2012 at 9:30 PM, Jonathan M Davis 
 <jmdavisProg gmx.com> wrote:

 Yes, that's what I wanted to propose. Group an AA and a 
 standard,
 dynamic, array. The array is just filled with the key, when you 
 assign
 a new key/value pair. To query the structure, use the AA. To 
 print the
 structure in order, iterate on this array of keys and query the 
 AA
 accordingly.

So one array like- aa[MyKey("abc","def","ghi")] = "my value"; and another like- string[] da; da[99]="abc"~","~"def"~","~"ghi"; or maybe- MyKey[] da; da[99]=MyKey("abc","def","ghi"); This thread has been quite helpful already. Thanks to all.
Aug 30 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Thu, Aug 30, 2012 at 10:24 PM, Paul <phshaffer gmail.com> wrote:
 So one array like-  aa[MyKey("abc","def","ghi")] = "my value";

 and another like- string[] da; da[99]="abc"~","~"def"~","~"ghi";
 or maybe-          MyKey[] da; da[99]=MyKey("abc","def","ghi");

The latter, if you group your keys, I think. You can just push the new value at the array's end, as if it was a stack: struct MyKey(string k1,k2,k3); string[MyKey] aa; MyKey[] da; // pushing "value" with key ("abc","def","ghi"): MyKey k = MyKey("abc","def","ghi"); aa[k] = "value"; da ~= k; // append k to da Jonathan's advice of recording the key's index (in the array) with the value is interesting, but then you need a special value in the array to indicate a discarded key. Here the recent thread about Option!T comes to mind... but I don't know what you're trying the achieve, so let's not go into complicated stuff right now.
 This thread has been quite helpful already.  Thanks to all

You're welcome. Note that your need of having a structure which is both associative and ordered is, if not unheard-of, at least somewhat uncommon.
Aug 30 2012
prev sibling next sibling parent "Paul" <phshaffer gmail.com> writes:
 You're welcome. Note that your need of having a structure which 
 is
 both associative and ordered is, if not unheard-of, at least 
 somewhat
 uncommon.

I'm parsing program blocks from a proprietary HW/SW system. They provide the data in the form of: Somegroupname/Someblockname someparam=value anotherparam=value ... otherparam=value end Somegroupname/Somediffblockname someparam=value anotherparam=value ... otherparam=value end Someothergroupname/Someotherblockname p1=value p2=value ... px=value end The data is in an ascii text file. I need to be able to search it by group/block/parameter. I need to be able to maintain group/block order. There are ~hundred diff block types where the params and order of params are known...though I would rather not create all of these structures or lists ahead of time. My greatest need at this point is to compare two files block by block. The blocks may be in diff orders between the files but the params of each block type would always be the same in the same order. So compare groups, blocks within groups, and the values of each param for matching group/block names.
Aug 31 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Fri, Aug 31, 2012 at 5:56 PM, Paul <phshaffer gmail.com> wrote:

 The data is in an ascii text file.
 I need to be able to search it by group/block/parameter.
 I need to be able to maintain group/block order.
 There are ~hundred diff block types where the params and order of params are
 known...though I would rather not create all of these structures or lists
 ahead of time.

 My greatest need at this point is to compare two files block by block.  The
 blocks may be in diff orders between the files but the params of each block
 type would always be the same in the same order.

 So compare groups, blocks within groups, and the values of each param for
 matching group/block names.

I see. In that case, your original idea of having a three-tier associative array (AA in AA in AA) is better than my tuple-key suggestion, since you want to look into all three levels. Does the provider ensure that no two groups have the same name and no two blocks in a group have the same name? If the blocks are in a different order, you don't care about order, right? Then use an AA. As params of each block always have the same order, you can use a dynamic array. So: struct Param { string name, value; } struct Block { string name; Param[] params; } struct Group { string name; Block[string] blocks; } alias Group[string] InputFile; InputFile file1, file2; Maybe someone here will have a better idea?
Aug 31 2012
prev sibling parent "Paul" <phshaffer gmail.com> writes:
On Saturday, 1 September 2012 at 05:23:35 UTC, Ali Çehreli wrote:
 On 08/31/2012 11:55 AM, Ali Çehreli wrote:

 class MyTable

[...]
 // Enables the 'auto element = myIndex in myTable' syntax

That's wrong. For that syntax to work, the operator below should have been opBinaryRight.
 string * opBinary(string op)(Index index)

Yeah, that should have been opBinaryRight. (And the badly designed Thunderbird removes the indentation in quoted text. Smart application or stupid designer?)
 // Enables 'auto value = myTable[myIndex]'
 ref string opIndex(Index index)
 {
 string * result = this.opBinary!"in"(index);

If I had defined opBinaryRight as I should have, then I could simply use the 'in' operator on the right-hand side: string * result = index in this;
 Ali

Ali "too"

Ali I am grateful for your help but its over my head...at least at present. Thanks.
Sep 06 2012