嗯,这很尴尬。
我的错误确实是基于一个误解:
class Item
{
internal string Value { get; set; }
internal string Qux { get; set; }
internal string Quux { get; set; }
}
var query = from i in list
group i by new { i.Value, i.Qux } into g // Note: no Quux, no primary key, just what's necessary
orderby g.Count() descending, g.Key.Value
select new { // [1]
Value = g.Key.Value,
Qux = g.Key.Qux,
// Quux?
}
我的错误假设是 [1] 处的选择作用于单个记录,就像 SQL 一样。好吧,希望我能拯救一个有同样假设的人:事实并非如此。
现在看起来很明显,但是选择作用于各个组,因此这里将为每个组创建一个新对象,而不是为每个元素创建一个新对象。
我的第二个错误是专注于密钥,想知道如何在不使用它的情况下“传递”其他属性。我还担心我们似乎需要对我们的对象进行浅拷贝。同样,这是基于我们对它的IGrouping
行为方式一无所知的事实:由于我们不知道我们正在选择组,我们甚至无法为选择中的每个元素创建新对象。
解决方案绝对不令人印象深刻:
var query = from i in list
group i by new { i.Value, i.Qux } into g
orderby g.Count() descending, g.Key.Value
select g;
foreach (var group in query)
{
foreach (var item in group)
Console.WriteLine("{0} {1} {2} [Original object? {3}]",
item.Value,
item.Qux,
item.Quux,
list.Contains(item));
Console.WriteLine("-");
}
输出:
AAA Foo ... [Original object? True]
AAA Foo ... [Original object? True]
AAA Foo ... [Original object? True]
-
BBB Foo ... [Original object? True]
BBB Foo ... [Original object? True]
-
AAA Bar ... [Original object? True]
-
CCC Foo ... [Original object? True]
-
DDD Foo ... [Original object? True]
-
DDD Bar ... [Original object? True]
-
EEE Foo ... [Original object? True]
事实上,IGrouping
元素是原始元素。从那里我们可以毫无问题地创建一个新列表。
更新:我在查询之外展平结果集,主要是为了能够将组分隔符写入控制台以进行演示,但请参阅 Tim 和 usr 的答案以使用SelectMany
(和等效的 Linq 语法)在查询中展平。
不用说,这是另一个经典案例“我们太着急了,让我们在这里和那里读一些例子就可以了”,正确的方法是花一点时间学习基础知识。Linq 不是 SQL。
对于您可能浪费时间试图消除混乱,我深表歉意。希望其他匆忙的人现在可以从这些错误中受益。