www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Simplest way to create an array from an associative array which its

reply "Gary Willoughby" <dev nomad.so> writes:
Simplest way to create an array from an associative array which 
its contains keys and values?

For example if i have an associative array like this:

["one":"1", "two":"2"]

What's the easiest way to create a dynamic array that looks like 
this:

["one", "1", "two", "2"]

I know it can be done via a loop, but is there a more idiomatic 
way to achieve this?
Jan 03 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Gary Willoughby:

 Simplest way to create an array from an associative array which 
 its contains keys and values?

 For example if i have an associative array like this:

 ["one":"1", "two":"2"]

 What's the easiest way to create a dynamic array that looks 
 like this:

 ["one", "1", "two", "2"]

 I know it can be done via a loop, but is there a more idiomatic 
 way to achieve this?
Unfortunately the D associative arrays specs don't specify this to be correct: zip(aa.byKey, aa.byValue) So a solution without foreach loops is: void main() { import std.stdio, std.algorithm, std.array; auto aa = ["one":"1", "two":"2"]; string[] r = aa.byKey.map!(k => [k, aa[k]]).join; r.writeln; } You can also use joiner if you need just a lazy range. Bye, bearophile
Jan 03 2014
next sibling parent reply "Andrej Mitrovic" <andrej.mitrovich gmail.com> writes:
On Friday, 3 January 2014 at 17:47:43 UTC, bearophile wrote:
     string[] r = aa.byKey.map!(k => [k, aa[k]]).join;
However the [k, aa[k]] expression will allocate an array for each key you iterate over regardless if you use join or joiner. I posted a solution with "only", hope that works. :)
Jan 03 2014
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 However the [k, aa[k]] expression will allocate an array for 
 each key you iterate over regardless if you use join or joiner. 
 I posted a solution with "only", hope that works. :)
Perhaps the "s" suffix (to define fixed-sized arrays) could avoid that problem: string[] r = aa.byKey.map!(k => [k, aa[k]]s).join; Bye, bearophile
Jan 05 2014
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 1/5/14, bearophile <bearophileHUGS lycos.com> wrote:
 Perhaps the "s" suffix (to define fixed-sized arrays) could avoid
 that problem:

 string[] r = aa.byKey.map!(k => [k, aa[k]]s).join;
I would prefer a prefix though, to make it immediately obvious you're creating a static array. string[] r = aa.byKey.map!(k => s[k, aa[k]]).join; It's not too bad.. But then again maybe tuples (and some syntax support) is the right solution for this.
Jan 05 2014
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
 string[] r = aa.byKey.map!(k => [k, aa[k]]s).join;
Do you remember the bug report number and/or pull request number for that enhancement? Bye, bearophile
Jan 09 2014
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/03/2014 09:47 AM, bearophile wrote:

 Unfortunately the D associative arrays specs don't specify this to be
 correct:

 zip(aa.byKey, aa.byValue)
I still like that solution. :) Even if it's not spelled out to be correct in the spec, I can't imagine a hash table implementation where byKey and byValue don't iterate in lock step. I wrote the following Expander range as an exercise. I think it could be useful in Phobos. (I am aware that there are proposals to revamp tuples in Phobos; the following is for 2.064.) Some issues: 1) Yes, expand may not be the best name as it would be confusing with Tuple.expand, which is a different thing. 2) As with some other InputRanges, the need to call the prime() member function up front in the constructor feels weird. It makes the range one-step eager. However, doing it in the front() conditionaly via 'if (is_primed)' would bring a cost to every call to front(). 3) The member rangeFront is needed because Tuple does not have opIndex for dynamic indexes. I can do range.front[0] but I cannot do range.front[currentIndex]. So, my solution was to take advantage of Tuple.expand by wrapping it in a slice, which I have taken out due to performance concern, without ever measuring anything. :p import std.range; void main() { auto aa = ["one":"1", "two":"2"]; assert(zip(aa.byKey, aa.byValue) .expand .equal([ "one", "1", "two", "2" ])); } /* Expands individual members of elements of a tuple-range as elements * of this range. */ struct Expander(R) if (__traits(compiles, [ ElementType!R ])) { alias ElementT = typeof([ ElementType!R.expand ].front); R range; ElementT[ElementType!R.length] rangeFront; size_t currentIndex; this(R range) { this.range = range; prime(); } private void prime() { if (!empty) { currentIndex = 0; /* The following static foreach "I think" avoids a dynamic array * allocation when compared to the following line: * * rangeFront = [ range.front.expand ]; */ foreach (i, element; range.front) { rangeFront[i] = element; } } } bool empty() property { return range.empty; } ElementT front() property { return rangeFront[currentIndex]; } void popFront() { ++currentIndex; if (currentIndex == ElementType!R.length) { range.popFront(); prime(); } } } Expander!R expand(R)(R range) if (__traits(compiles, [ ElementType!R ])) { return Expander!R(range); } unittest { import std.typecons; auto a = [ tuple(1, 2.2), tuple(3, 4.4) ]; auto expanded = a.expand; static assert(is (typeof(expanded.front) == double)); assert(expanded.equal([ 1, 2.2, 3, 4.4 ])); // Incompatible tuple members should fail to compile auto mismatched = [ tuple(int.init, string.init) ]; static assert(!__traits(compiles, mismatched.expand)); } Ali
Jan 03 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Ali Çehreli:

 I still like that solution. :) Even if it's not spelled out to 
 be correct in the spec, I can't imagine a hash table 
 implementation where byKey and byValue don't iterate in lock 
 step.
But in the long term ignoring what's present or missing in the specs is a bad idea.
 I wrote the following Expander range as an exercise.
See also: https://d.puremagic.com/issues/show_bug.cgi?id=5489 https://d.puremagic.com/issues/show_bug.cgi?id=7957 Bye, bearophile
Jan 03 2014
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/03/2014 04:39 PM, bearophile wrote:
 Ali Çehreli:

 I still like that solution. :) Even if it's not spelled out to be
 correct in the spec, I can't imagine a hash table implementation where
 byKey and byValue don't iterate in lock step.
But in the long term ignoring what's present or missing in the specs is a bad idea.
I don't mean to ignore. I would like to see the spec changed. This feels similar to first C++ standard never explicitly requiring that the elements of a string be contiguous in memory but every implementation doing so.
 I wrote the following Expander range as an exercise.
See also: https://d.puremagic.com/issues/show_bug.cgi?id=5489 https://d.puremagic.com/issues/show_bug.cgi?id=7957 Bye, bearophile
They make sense to me. Ali
Jan 03 2014
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Jan 04, 2014 at 12:39:32AM +0000, bearophile wrote:
 Ali Çehreli:
 
I still like that solution. :) Even if it's not spelled out to be
correct in the spec, I can't imagine a hash table implementation
where byKey and byValue don't iterate in lock step.
But in the long term ignoring what's present or missing in the specs is a bad idea.
I wrote the following Expander range as an exercise.
See also: https://d.puremagic.com/issues/show_bug.cgi?id=5489 https://d.puremagic.com/issues/show_bug.cgi?id=7957
[...] I did an implementation of aa.byPair once, that iterates over keys and values simultaneously, but it was ultimately rejected: https://github.com/D-Programming-Language/druntime/pull/574 T -- What's a "hot crossed bun"? An angry rabbit.
Jan 03 2014
parent "bearophile" <bearophileHUGS lycos.com> writes:
H. S. Teoh:

 I did an implementation of aa.byPair once, that iterates over 
 keys and
 values simultaneously, but it was ultimately rejected:
A byPair will be needed in Phobos, in one way or another. Bye, bearophile
Jan 03 2014
prev sibling parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sat, Jan 4, 2014 at 1:08 AM, Ali Çehreli <acehreli yahoo.com> wrote:

 3) The member rangeFront is needed because Tuple does not have opIndex for
 dynamic indexes. I can do range.front[0] but I cannot do
 range.front[currentIndex].
Is there any plan to add indexing on runtime indices to Tuple? It can be done, by generating a specific runtime opIndex for Tuple, if the types held in the tuple have a common type. CommonType!Types opIndex(size_t index) { // generated code, say Types is 3-element long: switch(index) { case 0: return values[0]; case 1: return values[1]; case 2: return values[2]; default: throw new OutOfRangeException(...); } }
Jan 05 2014
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01/05/2014 03:55 PM, Philippe Sigaud wrote:
 Is there any plan to add indexing on runtime indices to Tuple? It can
 be done, by generating a specific runtime opIndex for Tuple, if the
 types held in the tuple have a common type.
It would override the current index operator.
Jan 05 2014
parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sun, Jan 5, 2014 at 4:49 PM, Timon Gehr <timon.gehr gmx.ch> wrote:
 On 01/05/2014 03:55 PM, Philippe Sigaud wrote:
 Is there any plan to add indexing on runtime indices to Tuple? It can
 be done, by generating a specific runtime opIndex for Tuple, if the
 types held in the tuple have a common type.
It would override the current index operator.
Yes, indeed.
Jan 05 2014
prev sibling next sibling parent reply "Andrej Mitrovic" <andrej.mitrovich gmail.com> writes:
On Friday, 3 January 2014 at 17:38:16 UTC, Gary Willoughby wrote:
 I know it can be done via a loop, but is there a more idiomatic 
 way to achieve this?
---- import std.algorithm; import std.range; import std.stdio; void main() { auto aa = ["one":"1", "two":"2"]; auto range = aa.byKey.map!(a => chain(a.only, aa[a].only)); string[] array = range.join; writeln(array); } ---- However I'm not sure whether "only" is present in 2.064, maybe it was added recently in the git-head version.
Jan 03 2014
parent Justin Whear <justin economicmodeling.com> writes:
On Fri, 03 Jan 2014 17:49:28 +0000, Andrej Mitrovic wrote:

 On Friday, 3 January 2014 at 17:38:16 UTC, Gary Willoughby wrote:
 I know it can be done via a loop, but is there a more idiomatic way to
 achieve this?
---- import std.algorithm; import std.range; import std.stdio; void main() { auto aa = ["one":"1", "two":"2"]; auto range = aa.byKey.map!(a => chain(a.only, aa[a].only)); string[] array = range.join; writeln(array); } ---- However I'm not sure whether "only" is present in 2.064, maybe it was added recently in the git-head version.
`only` is available in 2.063, maybe even earlier.
Jan 03 2014
prev sibling parent reply "Craig Dillabaugh" <cdillaba cg.scs.carleton.ca> writes:
On Friday, 3 January 2014 at 17:38:16 UTC, Gary Willoughby wrote:
 Simplest way to create an array from an associative array which 
 its contains keys and values?

 For example if i have an associative array like this:

 ["one":"1", "two":"2"]

 What's the easiest way to create a dynamic array that looks 
 like this:

 ["one", "1", "two", "2"]

 I know it can be done via a loop, but is there a more idiomatic 
 way to achieve this?
As someone with little experience with functional programming, I am just curious - having browsed through the thread - if the various solutions proposed here would really be considered more 'idiomatic' D. Or if they were posted because the OP asked about avoiding the foreach() loop. In other words while: auto range = aa.byKey.map!(a => chain(a.only, aa[a].only)); string[] array = range.join; Saves a few lines of code, and looks cooler, it seems that the trivial foreach loop version is very easy: string[] array; foreach (key, value; aa) { array ~= key; array ~= value; }
Jan 07 2014
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jan 07, 2014 at 08:38:10PM +0000, Craig Dillabaugh wrote:
[...]
 As someone with little experience with functional programming, I am
 just curious - having browsed through the thread - if the various
 solutions proposed here would really be considered more 'idiomatic'
 D. Or if they were posted because the OP asked about avoiding the
 foreach() loop.
 
 In other words while:
 
     auto range = aa.byKey.map!(a => chain(a.only, aa[a].only));
     string[] array = range.join;
 
 Saves a few lines of code, and looks cooler, it seems that the
 trivial foreach loop version is very easy:
 
 string[] array;
 
 foreach (key, value; aa) {
 	array ~= key;
 	array ~= value;
 }
[...] Even better, encapsulate this in a function: CommonType!(K,V)[] aaToArray(K,V)(V[K] aa) if (is(CommonType!(V, K))) { typeof(return) result; foreach (key, value; aa) { result ~= key; result ~= value; } return result; } Then you can use it in a single line next time: string[] arr = aaToArray(["a": "aa", "b" : "bb"]); int[] arr = aaToArray([1: 2, 3: 4]); ... // etc. T -- Real men don't take backups. They put their source on a public FTP-server and let the world mirror it. -- Linus Torvalds
Jan 07 2014
parent "Craig Dillabaugh" <cdillaba cg.scs.carleton.ca> writes:
On Tuesday, 7 January 2014 at 20:52:40 UTC, H. S. Teoh wrote:
 On Tue, Jan 07, 2014 at 08:38:10PM +0000, Craig Dillabaugh 
 wrote:
 [...]
 As someone with little experience with functional programming, 
 I am
 just curious - having browsed through the thread - if the 
 various
 solutions proposed here would really be considered more 
 'idiomatic'
 D. Or if they were posted because the OP asked about avoiding 
 the
 foreach() loop.
 
 In other words while:
 
     auto range = aa.byKey.map!(a => chain(a.only, aa[a].only));
     string[] array = range.join;
 
 Saves a few lines of code, and looks cooler, it seems that the
 trivial foreach loop version is very easy:
 
 string[] array;
 
 foreach (key, value; aa) {
 	array ~= key;
 	array ~= value;
 }
[...] Even better, encapsulate this in a function: CommonType!(K,V)[] aaToArray(K,V)(V[K] aa) if (is(CommonType!(V, K))) { typeof(return) result; foreach (key, value; aa) { result ~= key; result ~= value; } return result; } Then you can use it in a single line next time: string[] arr = aaToArray(["a": "aa", "b" : "bb"]); int[] arr = aaToArray([1: 2, 3: 4]); ... // etc. T
Yes, I would imagine if this was not a 'one off' operation, you would likely want to create a function. That one looks nice. I posted my question mainly because D advertises itself as a 'multi-paradigm' language. It seems that a number of the more experienced posters on here seem to like functional approaches using the algorithms in std.algorithm. However, it seems to me sometimes the obvious/simple solution that avoids using std.algorithm results in more readable code. So I was curious to know if using std.algorithm functions are generally considered preferable for simple cases like this, or if it is simply a matter of taste. As an aside, the trade-off is even more blatant in C++ where a simple hand-rolled solution often comes out looking so much nicer than the STL <algorithm> alternative.
Jan 07 2014
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 1/7/14, Craig Dillabaugh <cdillaba cg.scs.carleton.ca> wrote:
 In other words while:

      auto range = aa.byKey.map!(a => chain(a.only, aa[a].only));
      string[] array = range.join;

 Saves a few lines of code, and looks cooler, it seems that the
 trivial foreach loop version is very easy:

 string[] array;

 foreach (key, value; aa) {
 	array ~= key;
 	array ~= value;
 }
OP asked for an array, however the reason I showed the example is because you can get a range that way, which is lazy and will not allocate: auto items = aa.byKey.map!(a => chain(a.only, aa[a].only)).joiner; // no allocations at all writeln(items); So there's a huge difference, as you're saving memory (and CPU time if you don't want to walk through the entire list).
Jan 07 2014
prev sibling parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Tuesday, 7 January 2014 at 20:38:11 UTC, Craig Dillabaugh 
wrote:
 Saves a few lines of code, and looks cooler, it seems that the 
 trivial foreach loop version is very easy:

 string[] array;

 foreach (key, value; aa) {
 	array ~= key;
 	array ~= value;
 }
Assuming the return value of `byKey` has a length property, using std.array.array has the benefit of automatically allocating just once for the result, while your proposed alternative will grow on demand, causing several allocations that depend on the number of pairs in `aa`. Of course, Teoh's `aaToArray` can trivially do this too, but the naive loop straight in user code is just that - naive.
Jan 07 2014