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

Ali Çehreli acehreli at yahoo.com
Fri Jan 3 16:08:36 PST 2014


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



More information about the Digitalmars-d-learn mailing list