如何从LINQ的IOrderedEnumerable提取元素并保留有序状态?
优雅提取前N个有序元素并保留IOrderedEnumerable类型的方案
方案1:伪排序实现无损耗转换
既然Take(10)后的元素已经是按原排序规则排好序的,我们可以用不改变顺序的伪排序生成IOrderedEnumerable,性能几乎无损耗,代码更简洁:
Dictionary<string, int> tagCounts = ... IOrderedEnumerable<KeyValuePair<string, int>> orderedTags = tagCounts .OrderByDescending(kvp => kvp.Value) .Take(10) .OrderBy(_ => 1); // 固定排序键,不会打乱原有顺序,同时返回IOrderedEnumerable
方案2:封装扩展方法复用逻辑
如果需要多次处理这类场景,可以封装一个扩展方法,把任意有序的IEnumerable转换成IOrderedEnumerable:
public static class EnumerableExtensions { public static IOrderedEnumerable<TSource> AsOrderedEnumerable<TSource>(this IEnumerable<TSource> source) { // 用固定值排序,保持原顺序 return source.OrderBy(_ => 1); } }
调用时更简洁:
IOrderedEnumerable<KeyValuePair<string, int>> orderedTags = tagCounts .OrderByDescending(kvp => kvp.Value) .Take(10) .AsOrderedEnumerable();
补充说明
Take()返回的OrderedPartition是.NET内部类型,它仅实现了IEnumerable<T>,没有实现IOrderedEnumerable<T>,所以无法直接强制转换。上面两种方案都是利用OrderBy返回IOrderedEnumerable<T>的特性,同时通过固定排序键避免打乱已有的顺序。
如果接口允许调整返回类型,也可以考虑将返回类型改为IEnumerable<KeyValuePair<string, int>>——因为Take()后的集合本身已经是有序的,IOrderedEnumerable<T>本质上是IEnumerable<T>的子类,若不需要后续追加排序逻辑,IEnumerable<T>完全可以满足需求。
内容的提问来源于stack exchange,提问作者Ian
相关产品推荐
相关产品推荐

