我想知道是否有更有效的方式从最初无序列表中按值获取有序的组列表,而不是使用GroupBy()
后跟OrderBy()
,像这样:
List<int> list = new List<int>();
IEnumerable<IEnumerable<int>> orderedGroups = list.GroupBy(x => x).OrderBy(x => x.Key);
有关详细信息,我有一个很大的List<T>
我想排序,但是有很多重复值,所以我想将结果返回为IEnumerable<IEnumerable<T>>
,就像{ {1}}返回GroupBy()
个组。如果我使用IEnumerable
,我只会获得OrderBy()
,但没有简单的方法可以知道值是否已从一个项目更改为下一个项目。我可以对列表进行分组,然后对组进行排序,但列表很大,因此最终变慢。由于IEnumerable<T>
会返回OrderBy()
,然后可以使用OrderedEnumerable
在辅助字段上对其进行排序,因此必须在内部区分具有相同或不同值的相邻项目。
我有什么方法可以利用ThenBy()
必须在内部按价值对结果进行分组的事实(为了方便OrderedEnumerable<T>
),或者其他什么是最有效的方法使用LINQ获取有序的组列表?
答案 0 :(得分:3)
您可以使用ToLookup,它会返回IEnumerable<IGrouping<TKey, TElement>
,然后根据需要对每个键的值执行OrderBy
。这将是O(n)来创建查找和O(h)来按顺序排列每个组下的元素(键的值),假设h是组下元素的数量
您可以使用IDictionary<TKey, IOrderedEnumerable<T>>
提高性能以分摊O(n)。但是如果你想通过多个属性进行排序,它将再次由O(h)组成。有关IOrderedEnumerable的更多信息,请参阅this answer。您也可以使用SortedList<TKey, TValue>
代替IOrderedEnumerable
[更新]:
以下是another answer,您可以查看一下。但同样,它涉及在结果之上执行OrderBy。
此外,您可以提出自己的数据结构,因为我没有看到BCL上可用的任何数据结构符合此要求。
一种可能的实施方式:
你可以有一个二进制搜索树,它平均在O(longN)中搜索/删除/插入。并且进行有序遍历将为您提供排序键。树上的每个节点都有一个有序集合,例如,值。
节点大致如下所示:
public class MyNode
{
prop string key;
prop SortedCollection myCollection;
}
您可以遍历初始集合一次并创建此特殊数据结构,可以查询该结构以获得快速结果。
[更新2]: 如果你有可能的密钥低于100k,那么我觉得实现你自己的数据结构是一种矫枉过正。通常,订单将返回非常快,所花费的时间很少。除非您有大量数据并且您多次订购,否则ToLookup应该可以很好地工作。
答案 1 :(得分:1)
老实说,你不会比
做得更好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<TKey, IList<TItem>>
,但我不认为你会变得更好大于开箱即用的LINQ给你的东西.LINQ
答案 2 :(得分:0)
我想在填充for(;;)
时通过列表Dictionary<T, int>
进行迭代,其中value是重复元素的计数会更快。