Thread overview
Using return type of a predicate function as a template
Oct 16, 2014
Edwin van Leeuwen
Oct 16, 2014
Atila Neves
Oct 16, 2014
Edwin van Leeuwen
October 16, 2014
I am trying to implement a groupBy function that groups by the return type of a predicate. Currently I have to define the returntype of the predicate for it to compile. Is there a way to get the return type at compile time and use it.

The code:
V[K] groupBy( alias func, K, V )( V values )
{
  V[K] grouped;
  foreach ( value ; values ) {
    grouped[func( value )] ~= value;
  }
  return grouped;
}

unittest {
  struct Test {
    string a;
    double b;
  }

  auto values = [Test( "a", 1 ), Test( "a", 2 ), Test( "b", 3 )];
  auto grouped = values.groupBy!( (a) => a.a, string );
  assert( grouped["a"].length == 2 );
  assert( grouped["a"][1].b == 2 );
  assert( grouped["b"].length == 1 );
  assert( grouped["b"][0].b == 3 );
}

So the above works, but I need to call it with:
values.groupBy!( (a) => a.a, string );
Ideally I would call it instead with:
values.groupBy!( (a) => a.a )
and it would infer that the template K needs to be a string, since that is the return type of (a) => a.a.

Cheers,

Edwin
October 16, 2014
This works:

import std.range;

auto groupBy(alias func, R)(R values)
    if (isInputRange!R)
{

    alias K = typeof(func(values.front));
    alias V = ElementType!R[];
    V[K] grouped;
    foreach(value; values) grouped[func(value)] ~= value;
    return grouped;
}


unittest {
  struct Test {
    string a;
    double b;
  }

  auto values = [Test( "a", 1 ), Test( "a", 2 ), Test( "b", 3 )];
  auto grouped = values.groupBy!(a => a.a);
  assert( grouped["a"].length == 2 );
  assert( grouped["a"][1].b == 2 );
  assert( grouped["b"].length == 1 );
  assert( grouped["b"][0].b == 3 );
}

Atila

On Thursday, 16 October 2014 at 08:04:08 UTC, Edwin van Leeuwen wrote:
> I am trying to implement a groupBy function that groups by the return type of a predicate. Currently I have to define the returntype of the predicate for it to compile. Is there a way to get the return type at compile time and use it.
>
> The code:
> V[K] groupBy( alias func, K, V )( V values )
> {
>   V[K] grouped;
>   foreach ( value ; values ) {
>     grouped[func( value )] ~= value;
>   }
>   return grouped;
> }
>
> unittest {
>   struct Test {
>     string a;
>     double b;
>   }
>
>   auto values = [Test( "a", 1 ), Test( "a", 2 ), Test( "b", 3 )];
>   auto grouped = values.groupBy!( (a) => a.a, string );
>   assert( grouped["a"].length == 2 );
>   assert( grouped["a"][1].b == 2 );
>   assert( grouped["b"].length == 1 );
>   assert( grouped["b"][0].b == 3 );
> }
>
> So the above works, but I need to call it with:
> values.groupBy!( (a) => a.a, string );
> Ideally I would call it instead with:
> values.groupBy!( (a) => a.a )
> and it would infer that the template K needs to be a string, since that is the return type of (a) => a.a.
>
> Cheers,
>
> Edwin

October 16, 2014
On Thursday, 16 October 2014 at 08:18:02 UTC, Atila Neves wrote:
> This works:
>
> import std.range;
>
> auto groupBy(alias func, R)(R values)
>     if (isInputRange!R)
> {
>
>     alias K = typeof(func(values.front));
>     alias V = ElementType!R[];
>     V[K] grouped;
>     foreach(value; values) grouped[func(value)] ~= value;
>     return grouped;
> }
>

Thank you, that is surprisingly straightforward :)

Edwin