已经有内置的 LINQ 方法 ElementAt,所以我们也可以有一个接受多个索引的 ElementsAt。
public static IEnumerable<TSource> ElementsAt<TSource>(
this IEnumerable<TSource> source, params int[] indices)
{
var indicesHashSet = new HashSet<int>(indices);
return source.Where((x, i) => indicesHashSet.Contains(i));
}
此实现按元素在源中的顺序返回元素,并忽略重复索引。
使用示例:
viewModel.SLGDeptSalesList =
viewModel.SLGDeptSalesList.ElementsAt(1, 3, 4, 6).ToList();
更新:这是一个替代实现。这个按索引的顺序返回元素,如果有重复的索引,则返回重复的元素。如果索引超出范围,则不会引发异常,但不会为这些索引返回任何元素。所以不能保证输出序列与请求的索引长度相等。
public static IEnumerable<TSource> ElementsAt<TSource>(
this IEnumerable<TSource> source, params int[] indices)
{
return indices.Join(source.Select((Item, Index) => (Item, Index)),
i => i, p => p.Index, (_, p) => p.Item);
}
更新:这里是先前实施的学术变体。这保证了请求的索引和输出序列将具有相等的长度。超出源序列范围的索引会引发异常。
public static IEnumerable<TSource> ElementsAt<TSource>(
this IEnumerable<TSource> source, params int[] indices)
{
var indicesHashSet = new HashSet<int>(indices);
var foundElements = source.Select((Item, Index) => (Item, Index))
.Where(p => indicesHashSet.Contains(p.Index))
.ToDictionary(p => p.Index, p => p.Item);
var notFoundIndices = indices.Where(i => !foundElements.ContainsKey(i)).ToList();
if (notFoundIndices.Count > 0)
throw new ArgumentOutOfRangeException(nameof(indices),
$"Indices [{String.Join(", ", notFoundIndices)}] " +
$"are outside the bounds of the {nameof(source)} sequence.");
return indices.Select(i => foundElements[i]);
}