Search code examples
c#group-byienumerableidictionary

How to convert IEnumerable<IList<T>> to IReadOnlyDictionary<string, IEnumerable<IList<T>>>?


Let's say I've a class Telemetry as follows:

public class Telemetry
{
   public string Id { get; set; }
   public AdditionalInfo Info { get; set; }
}

Let's say I've a class: TelemetryCollection that implements : IEnumerable<IList<Telemetry>>

I need to group an instance of TelemetryCollection based on Telemetry.Id and get a mapping of Telemetry.Id and TelemetryCollection

How do I get an IDictionary<string, IEnumerable<IList<Telemetry>>> from IEnumerable<IList<Telemetry>>>


Solution

  • Assuming TelemetryCollection contains arrays/lists of Telemetry measurements for the same Id with possible duplicates, you can:

    1. Group the arrays using group by the first array element's Id
    2. Use ToDictionary to collect the grouped arrays into a dictionary
    var dict=collection.GroupBy(t=>t[0].Id)
                       .ToDictionary(g=>g.Key,
                                     g=>g.AsEnumerable());
    

    The following example compiles:

    //Use a record to save keystrokes
    public record Telemetry(string Id ,int Info );
    
    IEnumerable<IList<Telemetry>> collection=new List<IList<Telemetry>>{
                new[]{ new Telemetry("A",1),new Telemetry("A",2)},
                new[]{ new Telemetry("B",1),new Telemetry("B",2)},
                new[]{ new Telemetry("A",3),new Telemetry("A",4)}
            };
    var dict=collection.GroupBy(t=>t[0].Id)
                       .ToDictionary(g=>g.Key,g=>g.AsEnumerable());
    Console.WriteLine(dict);
    

    And prints:

    System.Collections.Generic.Dictionary'2[System.String,System.Collections.Generic.IEnumerable'1[System.Collections.Generic.IList`1[ConsoleApp1.Telemetry]]]

    Serializing dict to JSON produces:

    {
       "A":[
              [{"Id":"A","Info":1},{"Id":"A","Info":2}], 
              [{"Id":"A","Info":3},{"Id":"A","Info":4}]
           ],
       "B":[
              [{"Id":"B","Info":1},{"Id":"B","Info":2}]
           ]
    }
    

    This won't work if there are any empty arrays in TelemetryCollection. This can be fixed if FirstOrDefault() is used to retrieve the first key and defaulting to "" if there's none :

    var dict=collection.GroupBy(t=>t.FirstOrDefault()?.Id??"")
                       .ToDictionary(g=>g.Key,
                                     g=>g.AsEnumerable());