根据组计数排序列表

时间:2013-10-22 12:20:10

标签: c# linq group-by

我想对List s的元素计数进行IGrouping排序。

就是这样,理想情况下列表应该是相同的。我会妥协新的列表,但是元素应该是完全相同的原始对象,而不是副本(不管是浅层),绝对不是匿名对象。

具体来说:我们有一个具有许多属性的实体,以及这种类型的对象列表。我们希望(1)按特定属性(名称,地址,...)对对象进行分组,然后(2)计算每个组中元素的数量。最后,我们希望(3)根据这些计数对列表进行重新排序,方法是首先放置属于较大组的元素。

注意:我们的主要问题是我们似乎无法找到一种方法来保持对组元素中原始对象的引用。实际上,我们可以在Linq查询中选择的是分组键(或键的属性),IGrouping不会显示任何其他内容。我们无法弄清楚如何将组元素与列表元素相关联,而不是查看数据(即便如此,我们还需要主键,我们无法将其添加到分组键或它会破坏分组开始的目的。)

3 个答案:

答案 0 :(得分:7)

var mySortedList = myList.GroupBy(x => x.Name).OrderByDescending(g => g.Count())
                       .SelectMany(x => x).ToList();

答案 1 :(得分:2)

.NET中几乎没有任何操作克隆对象。既不深也不浅。 LINQ也不会克隆它处理的元素。因此,一个简单的LINQ查询将起作用:

var oldList = ...;
var newList = (from x in oldList
               group x by something into g
               orderby g.Count()
               from x in g //flatten the groups
               select x).ToList();

此代码将引用复制到原始对象。如果你不相信,你可能会误解你所看到的。

答案 2 :(得分:0)

嗯,这很令人尴尬。

我的错误确实是基于一种误解:

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的行为方式一无所知:因为我们不知道我们在组上选择,所以我们甚至无法为select中的每个元素创建新对象

解决方案绝对不令人印象深刻:

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的答案以使用{在查询内部展平{1}}(和等效的Linq语法)。

毋庸置疑,这是另一个经典案例“我们太匆忙了,让我们来看看这里和那里的一些例子,那就是它”,正确的做法是花一点点一点时间学习基础知识。 Linq不是SQL。

我为你可能浪费的时间道歉,试图清除混乱。希望其他匆忙的人现在可以从这些错误中受益。