我有一个集合,特别是IList<T>
。我知道集合中的两个元素startElement
和endElement
。
是否有LINQ查询会将可枚举的startElement
返回到endElement
,包括在内?
我考虑过使用sequence.SkipWhile(p=>p!=startElement).TakeWhile(q=>q!=endElement)
但是错过了最后一个元素......
答案 0 :(得分:1)
我能想到的最好的是:
var subSection = TestData.SkipWhile(p => p != startElement).ToList();
var result = subSection.Take(subSection.IndexOf(endElement) + 1);
答案 1 :(得分:1)
George写了一个更灵活的扩展,你可以在这里找到它: https://stackoverflow.com/a/31940000/5106041
旧版本:
public static class MyExtensions
{
public static IEnumerable <TData> InBetween <TData> (this IEnumerable <TData> Target, TData StartItem, TData EndItem)
{
var Comparer = EqualityComparer <TData>.Default;
var FetchData = false;
var StopIt = false;
foreach (var Item in Target) {
if (StopIt)
break;
if (Comparer.Equals (Item, StartItem))
FetchData = true;
if (Comparer.Equals (Item, EndItem))
StopIt = true;
if (FetchData)
yield return Item;
}
yield break;
}
}
所以,现在你可以像这样使用它:
sequence.InBetween (startElement, endElement);
它不会迭代整个序列。 请注意,此处http://linqlib.codeplex.com/
中有大量读取扩展名答案 2 :(得分:1)
这不使用LINQ,但它可能是最简单/可读的方法。
int startIndex = sequence.IndexOf(startElement),
endIndex = sequence.IndexOf(endElement);
var range = sequence.GetRange(
startIndex,
// +1 to account for zero-based indexing
1 + endIndex - startIndex
);
请注意,这在技术上效率低于替代方案,但如果你已经在内存中有一个IList,差异可能会小于一毫秒,这对于可读代码来说是一个小小的牺牲。
但是,我建议使用秒表包装代码块以测试您的具体情况。
答案 3 :(得分:1)
这将是最有效的,因为它不会创建任何不必要的枚举器对象,只会遍历列表一次。
var result = new List<T>();
var inSequence = false;
for (var i = 0; i < list.Length; i++)
{
var current = list[i];
if (current == startElement) inSequence = true;
if (!inSequence) continue;
result.add(current);
if (current == endElement) break;
}
这不会处理缺少endElement
的情况,但您可以通过将result = null
指定为for
循环的最后一行i = list.Length - 1
来轻松完成此操作1}}
答案 4 :(得分:1)
我假设您不想使用额外的内存,并且不想超出底层迭代方法的算法复杂性,因此ToList,GroupBy,IndexOf不在我提议的实现中。
此外,不要在元素类型上设置约束,我使用谓词。
public static class EnumerableExtensions
{
/// <summary>
/// This one works using existing linq methods.
/// </summary>
public static IEnumerable<T> GetRange<T>(this IEnumerable<T> source, Func<T, bool> isStart, Func<T, bool> isStop)
{
var provideExtraItem = new[] { true, false };
return source
.SkipWhile(i => !isStart(i))
.SelectMany(i => provideExtraItem, (item, useThisOne) => new {item, useThisOne })
.TakeWhile(i => i.useThisOne || !isStop(i.item))
.Where(i => i.useThisOne)
.Select(i => i.item);
}
/// <summary>
/// This one is probably a bit faster.
/// </summary>
public static IEnumerable<T> GetRangeUsingIterator<T>(this IEnumerable<T> source, Func<T, bool> isStart, Func<T, bool> isStop)
{
using (var iterator = source.GetEnumerator())
{
while (iterator.MoveNext())
{
if (isStart(iterator.Current))
{
yield return iterator.Current;
break;
}
}
while (iterator.MoveNext())
{
yield return iterator.Current;
if (isStop(iterator.Current))
break;
}
}
}
}
这些方法可以用作扩展方法:
new[]{"apple", "orange", "banana", "pineapple"}.GetRange(i => i == "orange", i => i == "banana")