【问题标题】:Best way to get an ordered list of groups by value from an unordered list从无序列表中按值获取有序组列表的最佳方法
【发布时间】:2015-07-25 17:25:04
【问题描述】:

我想知道是否有比使用GroupBy() 后跟OrderBy() 更有效的方法从最初的无序列表中按值获取组的有序列表,如下所示:

List<int> list = new List<int>();
IEnumerable<IEnumerable<int>> orderedGroups = list.GroupBy(x => x).OrderBy(x => x.Key);

更多细节,我有一个大的List&lt;T&gt;,我想对其进行排序,但是有很多重复的值,所以我想将结果返回为IEnumerable&lt;IEnumerable&lt;T&gt;&gt;,就像GroupBy() 返回一个@987654327 @ 组。如果我使用OrderBy(),我只会得到IEnumerable&lt;T&gt;,没有简单的方法可以知道值是否从一项更改为下一项。我可以对列表进行分组,然后对组进行排序,但是列表很大,所以最终会很慢。由于OrderBy() 返回一个OrderedEnumerable,然后可以使用ThenBy() 在辅助字段上对其进行排序,因此它必须在内部区分具有相同或不同值的相邻项目。

有什么办法可以利用OrderedEnumerable&lt;T&gt; 必须在内部按值对结果进行分组的事实(以方便ThenBy()),否则使用 LINQ 获取有序列表的最有效方法是什么组?

【问题讨论】:

  • 发布您的代码会有所帮助。
  • 添加了一个示例实现,希望可以改进
  • 你能用 .Distinct() 吗?
  • 你能提供一些样本数据和想要的结果吗?
  • Mairaj,Distinct() 真的只是 GroupBy() 的一个特例,我不确定它在这里会有什么帮助

标签: c# linq


【解决方案1】:
  • 您可以使用ToLookup,它返回一个IEnumerable&lt;IGrouping&lt;TKey, TElement&gt;,然后根据需要对每个键的值执行OrderBy。这将是 O(n) 来创建查找和 O(h) 来排序每个组下的元素(键的值)假设 h 是组下的元素数

  • 您可以通过使用IDictionary&lt;TKey, IOrderedEnumerable&lt;T&gt;&gt; 来提高摊销 O(n) 的性能。但是,如果您想按多个属性排序,它将再次按组上的 O(h)。有关 IOrderedEnumerable 的更多信息,请参阅this answer。你也可以用SortedList&lt;TKey, TValue&gt;代替IOrderedEnumerable

[更新]:

这里是another answer,你可以看看。但同样,它涉及在结果之上执行 OrderBy。

此外,您可以提出自己的数据结构,因为我在 BCL 上没有看到任何满足此要求的数据结构。

一种可能的实现方式:

您可以拥有一个平均在 O(longN) 中搜索/删除/插入的二叉搜索树。并且进行有序遍历将为您提供排序的键。树上的每个节点都会有一个有序的集合,例如用于值。

节点大致如下:

public class MyNode
{
    prop string key;
    prop SortedCollection myCollection;
}

您可以遍历初始集合一次,并创建这种特殊的数据结构,可以对其进行查询以获得快速的结果。

[更新 2]: 如果您有可能低于 100k 的密钥,那么我觉得实现自己的数据结构是一种矫枉过正。通常,订单将很快返回,并且花费的时间很短。除非您有大量数据并且您多次订购,否则 ToLookup 应该可以很好地工作。

【讨论】:

  • 我真的很喜欢 ToLookup(),以前从来没有注意到这个方法。但是,在这种情况下,基于键的检索的好处无助于对键进行排序,因此即使我将 GroupBy() 替换为 ToLookup(),我仍然必须对整个 IEnumerable> 进行排序关键。
  • BCL 上没有开箱即用的 DataStructure 可以提供有序键和有序值。如果您使用字典/组,则无法保证键的顺序。你将不得不对它下订单。您可以提出自己的 DataStructure 存储有序键和有序 IList/IEnumerable 作为值值。
  • @Amit 是什么让你认为他需要有序值?
  • @Rawling,看起来你是对的,我读到 OrderedEnumerable 是有序值。
【解决方案2】:

老实说,你不会比你做得更好

items.GroupBy(i => i.KeyProperty).OrderBy(g => g.Key);

GroupBy 是一个O(n) 操作。那么OrderBy 就是O(k log k),其中k 是组数。

如果您先调用OrderBy...首先,您的O(n log n) 现在在您的项目数中,而不是在您的组数中,所以它已经比上面的慢了。

其次,IOrderedEnumerable 没有你认为的内在魔力。它不是包含相同排序项目组的有序序列,然后可以使用ThenBy 重新排序;它是一个无序序列,带有一个排序键列表,ThenBy添加到,当你迭代它时,最终由每个键排序。

您可以通过滚动自己的“分组和排序”循环来获得更快的速度,也许手动添加到SortedDictionary&lt;TKey, IList&lt;TItem&gt;&gt;,但我认为您不会得到更好的大 O比开箱即用的 LINQ 获得的更多。LINQ

【讨论】:

  • 我记得在某处读到 OrderedDictionary 是基于插入顺序索引的,并且名称应该是 IndexedDixtionary。只是需要注意课程。
  • @Amit 哎呀。我的意思是SortedDictionary。干杯让我查一下!
  • 在反映了所涉及的类之后,我可以看到您对 OrderedEnumerable 的看法是正确的,排序是使用快速排序执行的,它没有明确对具有相同值的项目进行分组
【解决方案3】:

我认为在填充 Dictionary&lt;T, int&gt; 时遍历列表 for(;;),其中 value 是重复元素的计数会更快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-23
    • 2016-06-27
    • 2017-05-20
    相关资源
    最近更新 更多