4

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

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

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

注意:我们的主要问题是我们似乎无法找到一种方法来保持对组元素中原始对象的引用。事实上,我们可以在 Linq 查询中选择的只是分组键(或键的属性),而IGrouping. 我们也无法弄清楚如何将组元素与列表的元素相关联,除非查看数据(即使这样,我们也需要主键,我们无法将其添加到分组键或它会破坏分组的目的)。

4

3 回答 3

9
var mySortedList = myList.GroupBy(x => x.Name).OrderByDescending(g => g.Count())
                       .SelectMany(x => x).ToList();
于 2013-10-22T13:44:45.773 回答
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();

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

于 2013-10-22T12:25:35.890 回答
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行为方式一无所知的事实:由于我们不知道我们正在选择组,我们甚至无法为选择中的每个元素创建新对象。

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

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。

对于您可能浪费时间试图消除混乱,我深表歉意。希望其他匆忙的人现在可以从这些错误中受益。

于 2013-10-22T13:39:04.710 回答