Thread overview
is there a merge for associative arrays
Nov 05, 2013
Daniel Davidson
Nov 05, 2013
bearophile
Nov 09, 2013
Daniel Davidson
Nov 05, 2013
Meta
November 05, 2013
The code below causes a crash. What is the idiomatic way to merge associative arrays? If there is a simple version that allows the value at a key to be clobbered by the value of the right hand operand when there is a collision, that is a start.

import std.stdio;
void main() {
  double[string] a = [ "foo" : 22.2 ];
  double[string] b = [ "bar" : 22.2 ];
  writeln(a+b);
}


Is there a way to do something like this and have opApply be called for '+'?
If so, is it a bad idea?

import std.stdio;

double[string] opApply(string op)(const double[string][] inputs ...)
  if(op == "+") {
  double[string] result;
  foreach( map ; inputs ) {
    foreach( key, value ; map ) {
      auto pval = key in result;
      if(pval) {
        *pval += value;
      } else {
        result[key] = value;
      }
    }
  }
  return result;
}

void main() {
  double[string] a = [ "foo" : 22.2 ];
  double[string] b = [ "bar" : 22.2 ];
  double[string] c = [ "bar" : 5 ];
  auto d = a+b+c;
}
November 05, 2013
Daniel Davidson:

> Is there a way to do something like this and have opApply be called for '+'?
> If so, is it a bad idea?
>
> import std.stdio;
>
> double[string] opApply(string op)(const double[string][] inputs ...)
>   if(op == "+") {
>   double[string] result;
>   foreach( map ; inputs ) {
>     foreach( key, value ; map ) {
>       auto pval = key in result;
>       if(pval) {
>         *pval += value;
>       } else {
>         result[key] = value;
>       }
>     }
>   }
>   return result;
> }
>
> void main() {
>   double[string] a = [ "foo" : 22.2 ];
>   double[string] b = [ "bar" : 22.2 ];
>   double[string] c = [ "bar" : 5 ];
>   auto d = a+b+c;
> }

In D operators need to be defined inside structs/classes.

So write a function, it could have signature as:

TV[TK] mergeAAs(TK, TV)(TV[TK] aas...) {

It seems even fit for Phobos.

Bye,
bearophile
November 05, 2013
Also, please report the crash in Bugzilla if you haven't already.

d.puremagic.com/issues/
November 09, 2013
On Tuesday, 5 November 2013 at 17:47:16 UTC, bearophile wrote:
> TV[TK] mergeAAs(TK, TV)(TV[TK] aas...) {
>
> It seems even fit for Phobos.
>
> Bye,
> bearophile

I have something I would appreciate feedback/criticism on. My first stab at it worked, but had no support for passing in const/immutable.

AA mergeAAs(alias fun = "a + b", AA)(AA[] aas...) if(isAssociativeArray!AA) {
  AA result;
  ...
}

Not sure if this is a good way or if there are better idiomatic ways, but here is what I have got. Any suggestions/improvements to make it more idiomatic would be appreciated. Is there already a DeepUnqual equivalent in phobos?

Thanks
Dan

import std.stdio;
import std.traits;
import std.algorithm;
import std.functional;

template DeepUnqual(T) {
  static if(isAssociativeArray!T) {
    alias Unqual!(Unqual!(ValueType!T)[Unqual!(KeyType!T)]) DeepUnqual;
  } else static if(isDynamicArray!T) {
    alias Unqual!(Unqual!(ArrayElementType!T)[]) DeepUnqual;
  } else static if(isPointer!T) {
    alias Unqual!(PointerTarget!T) * DeepUnqual;
  } else {
    alias Unqual!T DeepUnqual;
  }
}

DeepUnqual!AA
mergeAAs(alias fun = "a + b", AA)(AA[] aas...) if(isAssociativeArray!AA) {
  DeepUnqual!AA result;
  if(aas.length) {
    foreach( aa ; aas ) {
      foreach( k , v ; aa ) {
        auto found = k in result;
        if(found) {
          *found = binaryFun!fun(*found, v);
        } else {
          result[k] = v;
        }
      }
    }
  }
  return result;
}

void main() {
  alias double[string] AA;
  AA a = [ "foo":1.1, "bar":2.2 ];
  AA b = [ "foo":1.1, "bard":2.2 ];
  writeln(mergeAAs(a, b));
  writeln(mergeAAs!q{a*b}(a, b));

  {
    const AA ca = a.dup;
    const AA cb = b.dup;
    writeln(mergeAAs(ca,cb));
  }

  {
    immutable AA ia = [ "foo":1.1, "bar":2.2 ];
    immutable AA ib = [ "foo":1.1, "bard":2.2 ];
    writeln(mergeAAs(ia,ib));
  }
}