[英]C# LINQ OrderBy on sub array
使用 C# LINQ,我们是否可以只对 List 的一系列元素进行 OrderBy 并保留其他元素?
例如,输入列表是 {"a","b","c","d","e"},一个想象的 OrderByDescending 是这样的:
OrderByDescending(delegate d,int start_index,int end_index)
l=l.OrderByDescending(x=>x,1,3).ToList();
结果是:{"a","d","c","b","e"}
没有这个功能,我需要拆分/LINQ Orderby/重新加入,这失去了LINQ的精神。
您可以使用Skip
and Take
来实现这一点。
var input = new[] { "a", "b", "c", "d", "e" };
var res = input.Take(1)
.Concat(input.Skip(1).Take(3).OrderByDescending(e => e))
.Concat(input.Skip(4));
你也可以做一个像这样的扩展方法
public static class IEnumerableExt
{
public static IEnumerable<TSource> OrderRangeByDescending<TSource, TKey>(this IEnumerable<TSource> input, Func<TSource, TKey> keySelector, int from, int length)
{
return input.Take(from)
.Concat(input.Skip(from).Take(length).OrderByDescending(keySelector))
.Concat(input.Skip(from + length));
}
public static IEnumerable<TSource> OrderRangeBy<TSource, TKey>(this IEnumerable<TSource> input, Func<TSource, TKey> keySelector, int from, int length)
{
return input.Take(from)
.Concat(input.Skip(from).Take(length).OrderBy(keySelector))
.Concat(input.Skip(from + length));
}
}
var input = new[] { "a", "b", "c", "d", "e" };
var res = input.OrderRangeByDescending(e => e, 1, 3);
重复调用Skip
和Take
可能会影响性能,特别是如果源是由密集计算生成的。 最佳解决方案将要求源的读取仅发生一次。 这可以通过将源拆分为多个枚举来实现,但使用单个枚举器。 拥有这样一个 Splitter 将允许我们很容易地实现OrderRangeBy
/ OrderRangeByDescending
方法:
public static IEnumerable<TSource> OrderRangeBy<TSource, TKey>(
this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
int startIndex, int endIndexExclusive)
{
var parts = source.Split(startIndex, endIndexExclusive);
return parts[0].Concat(parts[1].OrderBy(keySelector)).Concat(parts[2]);
}
public static IEnumerable<TSource> OrderRangeByDescending<TSource, TKey>(
this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
int startIndex, int endIndexExclusive)
{
var parts = source.Split(startIndex, endIndexExclusive);
return parts[0].Concat(parts[1].OrderByDescending(keySelector)).Concat(parts[2]);
}
这是 Splitter 的一个实现:
public static IEnumerable<TSource>[] Split<TSource>(
this IEnumerable<TSource> source, params int[] indices)
{
var parts = new IEnumerable<TSource>[indices.Length + 1];
var enumerator = source.GetEnumerator();
int index = 0;
for (int i = 0; i < indices.Length; i++)
{
parts[i] = GetPart(indices[i]);
}
parts[indices.Length] = GetPart(Int32.MaxValue);
return parts;
IEnumerable<TSource> GetPart(int maxIndexExclusive)
{
if (index >= maxIndexExclusive) goto finish;
while (enumerator.MoveNext())
{
yield return enumerator.Current;
index++;
if (index >= maxIndexExclusive) break;
}
finish: if (maxIndexExclusive == Int32.MaxValue) enumerator.Dispose();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.