使用LINQ旋转数据
我试图看看我是否可以使用LINQ来解决我遇到的问题。 我有一个包含Enum(TypeCode)和User对象的项目集合,我需要将其展平以显示在网格中。 这很难解释,所以让我举一个简单的例子。
集合有这样的项目:
TypeCode | User --------------- 1 | Don Smith 1 | Mike Jones 1 | James Ray 2 | Tom Rizzo 2 | Alex Homes 3 | Andy Bates
我需要输出为:
1 | 2 | 3 Don Smith | Tom Rizzo | Andy Bates Mike Jones | Alex Homes | James Ray | |
感谢任何可以帮助我的人! 我尝试使用foreach这样做,但是我不能这样做,因为我要在foreach中将新项目插入到集合中,从而导致错误。
我并不是说它是一种很好的转向方式 – 但它是一个支点……
// 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(); }
Marc的答案给出了稀疏矩阵,不能直接泵入Grid。
我试图从Vasu提供的链接扩展代码,如下所示:
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()); }
*虽然不能说出表现。
您可以使用Linq的.ToLookup以您要查找的方式进行分组。
var lookup = data.ToLookup(d => d.TypeCode, d => d.User);
然后将它放入消费者可以理解的forms中。 例如:
//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); }
将它放在IEnumerable <>方法中,它将(可能)返回User的集合(列)的集合(行),其中null放在没有数据的列中。
我想这与Marc的答案类似,但我会发布它,因为我花了一些时间来处理它。 结果以" | "
分隔,如您的示例所示。 当使用group by而不是构造新的匿名类型时,它还使用从LINQ查询返回的IGrouping
类型。 这是经过测试的工作代码。
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();
我也用这个输入测试了它:
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"} };
其中产生了以下结果,表明第一列不需要包含最长的列表。 如果需要,您可以使用OrderBy
获取TypeCode排序的列。
1 | 3 | 2 Don Smith | Mike Jones | Tom Rizzo | James Ray | Alex Homes | Andy Bates |
@ Sanjaya.Tio我对你的回答很感兴趣,并创建了这个改编,最大限度地减少了keySelector的执行。 (另)
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; }