Dinamizar dados usando o LINQ

Eu estou tentando ver se posso usar o LINQ para resolver um problema que estou tendo. Eu tenho uma coleção de itens que contêm um Enum (TypeCode) e um object de usuário e preciso achatá-lo para mostrar em uma grade. É difícil explicar, então deixe-me mostrar um exemplo rápido.

Coleção tem itens assim:

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

Eu preciso que a saída seja:

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

Obrigado a todos que podem me ajudar! Eu tentei fazer isso usando foreach, mas eu não posso fazer dessa forma porque eu estaria inserindo novos itens para a coleção no foreach, causando um erro.

   

    Eu não estou dizendo que é uma ótima maneira de girar – mas é um pivô …

      // 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(); } 

    A resposta de Marc dá uma matriz esparsa que não pode ser bombeada diretamente para o Grid.
    Eu tentei expandir o código a partir do link fornecido pelo Vasu como abaixo:

     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()); } 

    * Não posso dizer nada sobre o desempenho embora.

    Você pode usar o .ToLookup do Linq para agrupar da maneira que você está procurando.

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

    Então é uma questão de colocá-lo em uma forma que seu consumidor possa entender. Por exemplo:

     //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); } 

    Coloque isso em um método IEnumerable <> e ele (provavelmente) retornará uma coleção (linhas) de coleções (coluna) do Usuário onde um nulo é colocado em uma coluna que não possui dados.

    Eu acho que isso é semelhante à resposta de Marc, mas vou postar desde que passei algum tempo trabalhando nisso. Os resultados são separados por " | " como no seu exemplo. Ele também usa o tipo IGrouping retornado da consulta LINQ ao usar um grupo por em vez de construir um novo tipo anônimo. Isso é testado, código de trabalho.

     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(); 

    Eu também testei com esta input:

     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 produziu os seguintes resultados, mostrando que a primeira coluna não precisa conter a lista mais longa. Você pode usar OrderBy para obter as colunas ordenadas por TypeCode, se necessário.

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

    @ Sanjaya.Tio Fiquei intrigado com a sua resposta e criei esta adaptação que minimiza a execução de keySelector. (não testado)

     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; }