Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq Aggregate complex types into a string

I've seen the simple example of the .net Aggregate function working like so:

string[] words = { "one", "two", "three" };
var res = words.Aggregate((current, next) => current + ", " + next);
Console.WriteLine(res);

How could the 'Aggregate' function be used if you wish to aggregate more complex types? For example: a class with 2 properties such as 'key' and 'value' and you want the output like this:

"MyAge: 33, MyHeight: 1.75, MyWeight:90"
like image 227
Myster Avatar asked Oct 02 '09 02:10

Myster


3 Answers

You have two options:

  1. Project to a string and then aggregate:

    var values = new[] {
        new { Key = "MyAge", Value = 33.0 },
        new { Key = "MyHeight", Value = 1.75 },
        new { Key = "MyWeight", Value = 90.0 }
    };
    var res1 = values.Select(x => string.Format("{0}:{1}", x.Key, x.Value))
                    .Aggregate((current, next) => current + ", " + next);
    Console.WriteLine(res1);
    

    This has the advantage of using the first string element as the seed (no prepended ", "), but will consume more memory for the strings created in the process.

  2. Use an aggregate overload that accepts a seed, perhaps a StringBuilder:

    var res2 = values.Aggregate(new StringBuilder(),
        (current, next) => current.AppendFormat(", {0}:{1}", next.Key, next.Value),
        sb => sb.Length > 2 ? sb.Remove(0, 2).ToString() : "");
    Console.WriteLine(res2);
    

    The second delegate converts our StringBuilder into a string, using the conditional to trim the starting ", ".

like image 54
dahlbyk Avatar answered Nov 13 '22 03:11

dahlbyk


Aggregate has 3 overloads, so you could use the one that has different type to accumulate the items you are enumerating.

You would need to pass in a seed value (your custom class), and a method to add merge the seed with one value. Example:

MyObj[] vals = new [] { new MyObj(1,100), new MyObj(2,200), ... };
MySum result = vals.Aggregate<MyObj, MySum>(new MySum(),
    (sum, val) =>
    {
       sum.Sum1 += val.V1;
       sum.Sum2 += val.V2;
       return sum;
    }
like image 25
Yuri Astrakhan Avatar answered Nov 13 '22 05:11

Yuri Astrakhan


The Aggregate function accepts a delegate parameter. You define the behavior you want by changing the delegate.

var res = data.Aggregate((current, next) => current + ", " + next.Key + ": " + next.Value);
like image 4
Joel Coehoorn Avatar answered Nov 13 '22 04:11

Joel Coehoorn