【问题标题】:Sort list based on group count根据组数排序列表
【发布时间】:2013-10-31 07:31:54
【问题描述】:

我想根据IGroupings 的元素countsList 进行排序。

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

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

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

【问题讨论】:

  • 不写文字,只写一个带有示例属性的示例类,然后告诉我们,您要按哪些属性分组,最后按什么顺序选择
  • 您可能缺少的是您的IGrouping 实际上也是该组中项目的可枚举。因此,一旦您对分组进行了排序,这只是将您的项目再次扁平化为单个列表的情况。
  • @ManishMishra:我个人认为这里不需要上课。实际上,该问题适用于任何具有单个属性的类。无论如何添加它有什么好处?
  • 给出准确的场景,具有相似的代码副本,减少了猜测工作,同时也激发了直接编写工作代码的兴奋。此外,c# 比英语更吸引我。
  • 非常感谢您的 cmets,考虑到问题的混乱,他们提供了令人惊讶的帮助。我在另一个答案中解释说,这确实是我的一个重大而根本的误解。

标签: c# linq group-by


【解决方案1】:

嗯,这很尴尬。

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

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

不用说,这是另一个经典案例,“我们太着急了,让我们在这里和那里读一些例子就可以了”,正确的方法是花一点时间一点时间学习基础知识。 Linq 不是 SQL。

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

【讨论】:

  • 希望您已经看到了 Tim S. 的简洁的生成排序列表的方法。考虑到任务听起来多么复杂,它出奇地整洁。 :)
  • 确实有,之前对SelectMany不太熟悉。实际上 usr 的答案几乎相同,他的代码也转换为 SelectMany。这是一些快速学习。不过,'应该做出更好的初步努力。
  • 我对查询语法不太熟悉,个人认为它看起来不太可读,这就是我提到 Tim 的原因。但只要你学到了一些东西,那么一切都是值得的。 ;-)
【解决方案2】:
var mySortedList = myList.GroupBy(x => x.Name).OrderByDescending(g => g.Count())
                       .SelectMany(x => x).ToList();

【讨论】:

    【解决方案3】:

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

    此代码将引用复制到原始对象。如果您不这么认为,那么您可能会误解您所看到的内容。

    【讨论】:

      猜你喜欢
      • 2017-12-25
      • 1970-01-01
      • 2021-11-24
      • 1970-01-01
      • 1970-01-01
      • 2018-12-08
      • 2021-08-04
      • 2015-08-01
      • 1970-01-01
      相关资源
      最近更新 更多