Jump to page: 1 2 3
Thread overview
Simplest way to create an array from an associative array which its contains keys and values?
Jan 03, 2014
Gary Willoughby
Jan 03, 2014
bearophile
Jan 03, 2014
Andrej Mitrovic
Jan 05, 2014
bearophile
Jan 05, 2014
Andrej Mitrovic
Jan 09, 2014
bearophile
Jan 04, 2014
Ali Çehreli
Jan 04, 2014
bearophile
Jan 04, 2014
Ali Çehreli
Jan 04, 2014
H. S. Teoh
Jan 04, 2014
bearophile
Jan 05, 2014
Philippe Sigaud
Jan 05, 2014
Timon Gehr
Jan 05, 2014
Philippe Sigaud
Jan 03, 2014
Andrej Mitrovic
Jan 03, 2014
Justin Whear
Jan 07, 2014
Craig Dillabaugh
Jan 07, 2014
H. S. Teoh
Jan 07, 2014
Craig Dillabaugh
Jan 07, 2014
Andrej Mitrovic
Jan 07, 2014
Jakob Ovrum
January 03, 2014
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?
January 03, 2014
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
January 03, 2014
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.
January 03, 2014
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. :)
January 03, 2014
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.
January 04, 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

January 04, 2014
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
January 04, 2014
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

January 04, 2014
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.
January 04, 2014
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
« First   ‹ Prev
1 2 3