Datos pivote usando LINQ

Estoy tratando de ver si puedo usar LINQ para resolver un problema que estoy teniendo. Tengo una colección de elementos que contienen un Enum (TypeCode) y un objeto User, y necesito aplanarlo para mostrarlo en una grilla. Es difícil de explicar, así que déjame mostrarte un ejemplo rápido.

La colección tiene elementos como estos:

TypeCode | User --------------- 1 | Don Smith 1 | Mike Jones 1 | James Ray 2 | Tom Rizzo 2 | Alex Homes 3 | Andy Bates 

Necesito que la salida sea:

 1 | 2 | 3 Don Smith | Tom Rizzo | Andy Bates Mike Jones | Alex Homes | James Ray | | 

¡Gracias a todos los que pueden ayudarme! Intenté hacer esto usando foreach, pero no puedo hacerlo de esa manera porque insertaría nuevos elementos en la colección en el foreach, lo que provocaría un error.

No digo que sea una gran manera de pivotar, pero es un pivote …

  // sample data var data = new[] { new { Foo = 1, Bar = "Don Smith"}, new { Foo = 1, Bar = "Mike Jones"}, new { Foo = 1, Bar = "James Ray"}, new { Foo = 2, Bar = "Tom Rizzo"}, new { Foo = 2, Bar = "Alex Homes"}, new { Foo = 3, Bar = "Andy Bates"}, }; // group into columns, and select the rows per column var grps = from d in data group d by d.Foo into grp select new { Foo = grp.Key, Bars = grp.Select(d2 => d2.Bar).ToArray() }; // find the total number of (data) rows int rows = grps.Max(grp => grp.Bars.Length); // output columns foreach (var grp in grps) { Console.Write(grp.Foo + "\t"); } Console.WriteLine(); // output data for (int i = 0; i < rows; i++) { foreach (var grp in grps) { Console.Write((i < grp.Bars.Length ? grp.Bars[i] : null) + "\t"); } Console.WriteLine(); } 

La respuesta de Marc da una matriz dispersa que no se puede bombear directamente a Grid.
Traté de expandir el código desde el enlace proporcionado por Vasu de la siguiente manera:

 public static Dictionary> Pivot3( this IEnumerable source , Func key1Selector , Func key2Selector , Func, TValue> aggregate) { return source.GroupBy(key1Selector).Select( x => new { X = x.Key, Y = source.GroupBy(key2Selector).Select( z => new { Z = z.Key, V = aggregate(from item in source where key1Selector(item).Equals(x.Key) && key2Selector(item).Equals(z.Key) select item ) } ).ToDictionary(e => eZ, o => oV) } ).ToDictionary(e => eX, o => oY); } internal class Employee { public string Name { get; set; } public string Department { get; set; } public string Function { get; set; } public decimal Salary { get; set; } } public void TestLinqExtenions() { var l = new List() { new Employee() { Name = "Fons", Department = "R&D", Function = "Trainer", Salary = 2000 }, new Employee() { Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3000 }, new Employee() { Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 4000 }, new Employee() { Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5000 }, new Employee() { Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6000 }, new Employee() { Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 2000 }}; var result5 = l.Pivot3(emp => emp.Department, emp2 => emp2.Function, lst => lst.Sum(emp => emp.Salary)); var result6 = l.Pivot3(emp => emp.Function, emp2 => emp2.Department, lst => lst.Count()); } 

* no puedo decir nada sobre el rendimiento sin embargo.

Puede usar .ToLookup de Linq para agrupar de la manera que está buscando.

 var lookup = data.ToLookup(d => d.TypeCode, d => d.User); 

Entonces, se trata de ponerlo en una forma que su consumidor pueda darle sentido. Por ejemplo:

 //Warning: untested code var enumerators = lookup.Select(g => g.GetEnumerator()).ToList(); int columns = enumerators.Count; while(columns > 0) { for(int i = 0; i < enumerators.Count; ++i) { var enumerator = enumerators[i]; if(enumator == null) continue; if(!enumerator.MoveNext()) { --columns; enumerators[i] = null; } } yield return enumerators.Select(e => (e != null) ? e.Current : null); } 

Ponlo en un método IEnumerable <> y (probablemente) devolverá una colección (filas) de colecciones (columna) de Usuario donde se pone un valor nulo en una columna que no tiene datos.

Supongo que esto es similar a la respuesta de Marc, pero lo publicaré ya que pasé un tiempo trabajando en ello. Los resultados están separados por " | " como en su ejemplo. También utiliza el tipo IGrouping devuelto de la consulta LINQ al usar un grupo en lugar de construir un nuevo tipo anónimo. Esto es probado, código de trabajo.

 var Items = new[] { new { TypeCode = 1, UserName = "Don Smith"}, new { TypeCode = 1, UserName = "Mike Jones"}, new { TypeCode = 1, UserName = "James Ray"}, new { TypeCode = 2, UserName = "Tom Rizzo"}, new { TypeCode = 2, UserName = "Alex Homes"}, new { TypeCode = 3, UserName = "Andy Bates"} }; var Columns = from i in Items group i.UserName by i.TypeCode; Dictionary> Rows = new Dictionary>(); int RowCount = Columns.Max(g => g.Count()); for (int i = 0; i <= RowCount; i++) // Row 0 is the header row. { Rows.Add(i, new List()); } int RowIndex; foreach (IGrouping c in Columns) { Rows[0].Add(c.Key.ToString()); RowIndex = 1; foreach (string user in c) { Rows[RowIndex].Add(user); RowIndex++; } for (int r = RowIndex; r <= Columns.Count(); r++) { Rows[r].Add(string.Empty); } } foreach (List row in Rows.Values) { Console.WriteLine(row.Aggregate((current, next) => current + " | " + next)); } Console.ReadLine(); 

También lo probé con esta entrada:

 var Items = new[] { new { TypeCode = 1, UserName = "Don Smith"}, new { TypeCode = 3, UserName = "Mike Jones"}, new { TypeCode = 3, UserName = "James Ray"}, new { TypeCode = 2, UserName = "Tom Rizzo"}, new { TypeCode = 2, UserName = "Alex Homes"}, new { TypeCode = 3, UserName = "Andy Bates"} }; 

Que produjo los siguientes resultados que muestran que la primera columna no necesita contener la lista más larga. Puede usar OrderBy para obtener las columnas ordenadas por TypeCode si es necesario.

 1 | 3 | 2 Don Smith | Mike Jones | Tom Rizzo | James Ray | Alex Homes | Andy Bates | 

@ Sanjaya.Tio Me intrigó tu respuesta y creé esta adaptación que minimiza la ejecución de keySelector. (no probado)

 public static Dictionary> Pivot3( this IEnumerable source , Func key1Selector , Func key2Selector , Func, TValue> aggregate) { var lookup = source.ToLookup(x => new {Key1 = keySelector1(x), Key2 = keySelector2(x)}); List key1s = lookup.Select(g => g.Key.Key1).Distinct().ToList(); List key2s = lookup.Select(g => g.Key.Key2).Distinct().ToList(); var resultQuery = from key1 in key1s from key2 in key2s let lookupKey = new {Key1 = key1, Key2 = key2} let g = lookup[lookupKey] let resultValue = g.Any() ? aggregate(g) : default(TValue) select new {Key1 = key1, Key2 = key2, ResultValue = resultValue}; Dictionary> result = new Dictionary>(); foreach(var resultItem in resultQuery) { TKey1 key1 = resultItem.Key1; TKey2 key2 = resultItem.Key2; TValue resultValue = resultItem.ResultValue; if (!result.ContainsKey(key1)) { result[key1] = new Dictionary(); } var subDictionary = result[key1]; subDictionary[key2] = resultValue; } return result; }