Trying to pivot data using linq

Pete Dragun picture Pete Dragun · Aug 14, 2013 · Viewed 12.4k times · Source

I have the following class structure:

 public class TaskLog
 {
    public DateTime LogDate { get; set; }
    public string Robot { get; set; }
    public string Task { get; set; }
    public Enums.ProbeDataState State { get; set; }

    public TaskLog()
    {
    }
}

I created a generic list as follows:

List<TaskLog> Logs;

My output:

Robot   Date                    State
---------------------------------------------
aaa     8/5/2013 12:00:00 AM    Task:1=fileDeltaFailed
aaa     8/5/2013 12:00:00 AM    Task:2=fileDeltaFailed
aaa     8/5/2013 12:00:00 AM    Task:4=fileDeltaFailed
bbb     8/5/2013 12:00:00 AM    Task:1=fileDeltaFailed
bbb     8/5/2013 12:00:00 AM    Task:2=fileDeltaFailed
bbb     8/5/2013 12:00:00 AM    Task:4=fileDeltaFailed

However I would like to group the tasks and state as follows:

                                Tasks
Robot   Date                    1                 2                4
aaa     8/5/2013 12:00:00 AM    fileDeltaFailed   fileDeltaFailed  fileDeltaFailed 
bbb     8/5/2013 12:00:00 AM    fileDeltaFailed   fileDeltaFailed  fileDeltaFailed

I tried using the groupby with no luck, and quite stuck.

e.g.

 var dataQuery = Logs.Where(w => w.LogDate >= start && w.LogDate <= finish).GroupBy(g => g.LogDate).Select(t => new
            {
                LogDate = t.Key,
                Details = t.OrderBy(o => o.Robot)
            }).ToList();

Answer

nmclean picture nmclean · Aug 14, 2013

If I understand the question, you want a mapping of Task to State for each Robot. You could group by Robot and select a dictionary for each group:

Logs.GroupBy(t => t.Robot).Select(g => new {
    Robot = g.Key,
    TaskStates = g.ToDictionary(t => t.Task, t => t.State)
})

This assumes that task names are unique for each robot (ToDictionary would throw an exception otherwise).

You could also add another level of grouping for dates:

Logs.GroupBy(t => t.LogDate).Select(g => new {
    Date = g.Key,
    Details = g.GroupBy(t => t.Robot).Select(g => new {
        Robot = g.Key,
        TaskStates = g.ToDictionary(t => t.Task, t => t.State)
    }).ToList()
})

Note that the Details property is essentially equivalent to my first example, the only difference being that it queries the outer grouping instead of the whole sequence. The result is a sequence of {Date, Details} where each "detail" is a list of {Robot, TaskStates}.

I haven't tested this, so let me know if there are any bugs I missed.