Ruota i dati usando LINQ

Sto cercando di vedere se posso usare LINQ per risolvere un problema che sto avendo. Ho una collezione di oggetti che contengono un Enum (TypeCode) e un object User, e ho bisogno di appiattirlo per mostrare in una griglia. È difficile da spiegare, quindi lascia che ti mostri un esempio veloce.

La raccolta ha oggetti come questi:

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

Ho bisogno che l’output sia:

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

Grazie a tutti coloro che possono aiutarmi! Ho provato a farlo utilizzando foreach, ma non posso farlo in questo modo perché inserirò nuovi elementi nella raccolta nel foreach, causando un errore.

    Non sto dicendo che sia un ottimo modo per ruotare – ma è un perno …

      // 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 risposta di Marc fornisce una matrice sparsa che non può essere pompata direttamente in Grid.
    Ho provato ad espandere il codice dal link fornito da Vasu come di seguito:

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

    * non posso dire nulla sulla performance però.

    Puoi usare Linq’s .ToLookup per raggruppare nel modo che stai cercando.

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

    Quindi è questione di metterlo in una forma che il tuo consumatore possa dare un senso. Per esempio:

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

    Mettilo in un metodo IEnumerable <> e restituirà (probabilmente) una collezione (righe) di collezioni (colonne) di Utente dove un null è inserito in una colonna che non ha dati.

    Immagino che questo sia simile alla risposta di Marc, ma lo posterò da quando ho passato un po ‘di tempo a lavorarci su. I risultati sono separati da " | " come nel tuo esempio. Usa anche il IGrouping restituito dalla query LINQ quando si utilizza un gruppo anziché build un nuovo tipo anonimo. Questo è testato, codice funzionante.

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

    L’ho anche testato con questo 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"} }; 

    Che ha prodotto i seguenti risultati che mostrano che la prima colonna non ha bisogno di contenere la lista più lunga. È ansible utilizzare OrderBy per ottenere le colonne ordinate da TypeCode se necessario.

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

    @ Sanjaya.Tio Sono rimasto affascinato dalla tua risposta e ho creato questo adattamento che minimizza l’esecuzione di keySelector. (non testato)

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