C# 是否有一种方法可以使用LINQ根据封闭条件(即不是简单的WHERE子句)选择一系列项?
假设您有一个对象的C# 是否有一种方法可以使用LINQ根据封闭条件(即不是简单的WHERE子句)选择一系列项?,c#,linq,lambda,C#,Linq,Lambda,假设您有一个对象的列表,并且Foo有一个IsSelected属性,比如 public class Foo { public string Name{ get; set; } public bool IsSelected{ get; set; } } List<Foo> sourceItems = new List<Foo> { new Foo(){ Name="First", IsSelected=false}, new Foo(){
列表
,并且Foo
有一个IsSelected
属性,比如
public class Foo
{
public string Name{ get; set; }
public bool IsSelected{ get; set; }
}
List<Foo> sourceItems = new List<Foo>
{
new Foo(){ Name="First", IsSelected=false},
new Foo(){ Name="Second", IsSelected=true },
new Foo(){ Name="Third", IsSelected=false},
new Foo(){ Name="Fourth", IsSelected=true },
new Foo(){ Name="Fifth", IsSelected=false},
new Foo(){ Name="Sixth", IsSelected=true },
new Foo(){ Name="Seventh", IsSelected=true },
new Foo(){ Name="Eighth", IsSelected=false},
new Foo(){ Name="Ninth", IsSelected=false},
new Foo(){ Name="Tenth", IsSelected=false}
};
var results = sourceItems.Where(item => item.IsSelected);
…但是如果我想要第一个项目和最后一个项目之间的所有项目,其中IsSelected为true,该怎么办?(即第二次到第七次)
我知道我可以使用SkipWhile,因为它会一直跳到第一个true语句,然后返回后面的所有内容
// Returns from Second on
var results = sourceItems.SkipWhile(item => !item.IsSelected);
…我知道我可以倒车,然后再做同样的事情,但最后我不得不重新倒车,而重复倒车感觉成本太高了
我的另一个想法是使用Select with索引并将最后一个索引存储在ISSELECT为true的位置,然后在末尾使用where子句,检查该索引是否低于最后一个选定的索引,但这看起来既昂贵又笨拙
int lastSelectedIndex = -1;
var results = sourceItems
.SkipWhile(item => !item.IsSelected)
.Select( (item, itemIndex) =>
{
if(item.IsSelected)
lastSelectedIndex = index;
return new {item, index};
})
.Where(anonObj => anonObj.index <= lastSelectedIndex)
.Select(anonObj => anonObj.Item);
那么有没有其他方法来实现我的要求呢?您可以在Where中传递索引,并在条件中使用它
var result = sourceItems.Where((ele, index) =>
index > 0 && index < 7 && ele.IsSelected);
你最初的直觉是对的。请这样做:
var results =
sourceItems
.SkipWhile(x => x.IsSelected == false)
.Reverse()
.SkipWhile(x => x.IsSelected == false)
.Reverse();
除非您的列表包含数百万项,否则它不会有太大的性能问题
我刚刚用列表中的1000000个元素尝试了这段代码,它在我三岁的笔记本电脑上用163毫秒就完成了。10000000只花了1.949秒。您可以使用
.Aggregte
执行此操作,并且只迭代一次,但有点混乱:
var lists = sourceItems.Aggregate(Tuple.Create(new List<Foo>(), new List<Foo>()), (acc, foo) =>
{
if (foo.IsSelected)
{
acc.Item1.AddRange(acc.Item2);
acc.Item2.Clear();
acc.Item2.Add(foo);
}
else if (acc.Item2.Any())
{
acc.Item2.Add(foo);
}
return acc;
});
if (lists.Item2.Any()) lists.Item1.Add(lists.Item2.First());
您甚至可以将其更改为返回另一个列表,并将
yield
s替换为List。如果需要,请添加。好吧,让我通过使用索引和将项目添加到新集合来不同地处理此问题
List<Foo> sourceItems = new List<Foo>
{
new Foo(){ Name="First", IsSelected=false},
new Foo(){ Name="Second", IsSelected=true },
new Foo(){ Name="Third", IsSelected=false},
new Foo(){ Name="Fourth", IsSelected=true },
new Foo(){ Name="Fifth", IsSelected=false},
new Foo(){ Name="Sixth", IsSelected=true },
new Foo(){ Name="Seventh", IsSelected=true },
new Foo(){ Name="Eighth", IsSelected=false},
new Foo(){ Name="Ninth", IsSelected=false},
new Foo(){ Name="Tenth", IsSelected=false}
};
int startIndex = sourceItems.FindIndex(x => x.IsSelected);
int endIndex = sourceItems.FindLastIndex(x => x.IsSelected);
var items = new List<Foo>();
for (int i = startIndex; i <= endIndex; i++)
items.Add(sourceItems[i]);
List sourceItems=新列表
{
new Foo(){Name=“First”,IsSelected=false},
new Foo(){Name=“Second”,IsSelected=true},
new Foo(){Name=“Third”,IsSelected=false},
new Foo(){Name=“Fourth”,IsSelected=true},
new Foo(){Name=“Fifth”,IsSelected=false},
new Foo(){Name=“Sixth”,IsSelected=true},
new Foo(){Name=“Seventh”,IsSelected=true},
new Foo(){Name=“第八”,IsSelected=false},
new Foo(){Name=“nixth”,IsSelected=false},
new Foo(){Name=“Tenth”,IsSelected=false}
};
int startIndex=sourceItems.FindIndex(x=>x.IsSelected);
int endIndex=sourceItems.FindLastIndex(x=>x.IsSelected);
var items=新列表();
对于(inti=startIndex;i以及Dimi Toulakis
如果有人经常在他的代码中需要它,这里有一个扩展
public static class ListExtension
{
public static List<T> FindGroup<T>(this List<T> mylist, Predicate<T> pred)
{
var first = mylist.FindIndex(pred);
var last = mylist.FindLastIndex(pred);
last += 1; // to get the Last Element
return mylist.GetRange(first, last - first);
}
}
公共静态类ListExtension
{
公共静态列表FindGroup(此列表mylist,谓词pred)
{
var first=mylist.FindIndex(pred);
var last=mylist.findlastinex(pred);
last+=1;//获取最后一个元素
返回mylist.GetRange(first,last-first);
}
}
组合和:
List sourceItems=新列表{
new Foo(){Name=“First”,IsSelected=false},
new Foo(){Name=“Second”,IsSelected=true},
new Foo(){Name=“Third”,IsSelected=false},
new Foo(){Name=“Fourth”,IsSelected=true},
new Foo(){Name=“Fifth”,IsSelected=false},
new Foo(){Name=“Sixth”,IsSelected=true},
new Foo(){Name=“Seventh”,IsSelected=true},
new Foo(){Name=“第八”,IsSelected=false},
new Foo(){Name=“nixth”,IsSelected=false},
new Foo(){Name=“Tenth”,IsSelected=false}
};
int startIndex=sourceItems.FindIndex(item=>item.IsSelected);
int endIndex=sourceItems.findlastedex(item=>item.IsSelected);
var result=sourceItems。其中((item,itemIndex)=>itemIndex>=startIndex&&itemIndex这是一个追逐性能并不总是有效的好例子。此解决方案产生正确的结果,但比“双反转”慢方法。我花了2.267秒来处理10000000个项目的列表,相比之下,使用双反转方法需要1.949秒。@Enigmativity如果您想要性能,那么LINQ不是最好的方法-只需一直使用命令行即可(请参阅我的编辑)哦,是的,我完全同意你的观点。我的观点是,只有当列表中有数百万个对象时,性能才会成为一个问题。因此,使用最简单的LINQ通常是一种方法。命令式几乎总是更快,但更难维护。你可以一直走到极端,在汇编程序中编写代码-这是最快的-但我t不可维护。99.9%的时间使用简单LINQ。@卡斯滕,你的编辑看起来很有趣,但我在跟踪双收益回报率方面有点困难,特别是在最初的一眼中,它似乎是按相反的顺序返回的。你也有Jyield
将返回带有IsSelected
标志的元素-之前的循环将生成从最后一个IsSelected
-元素(lastSelected=i+1
)之后到当前(j
)之前的所有元素-如果你试着像j Dude那样做…谢谢,但这有点离题。这是我从脑海中键入的伪代码,用来显示我们的问题,不是实际代码。尽管如此,我还是做了更改。而且,你的答案似乎没有解决我在这里要找的问题,因为我不确定你从哪里得到了0和7。我从OP中得到了0和7(即第二次到第七次),需要哪些元素?是否要先排除第一个和最后一个,然后检查哪些对象的IsSelected=true,或者首先要筛选IsSelected=true的所有对象,然后排除第一个和最后一个?否,范围基于IsSelected设置为true的第一个和最后一个项目。如我的问题详细信息所述,我正在查找g第一个和最后一个选择的项目之间的项目范围,无论是否也选择了中间的项目。希望有意义。检查我的更新,我认为这将为您提供所需的输出,但我不确定效率如何
static IEnumerable<Foo> BetweenSelected(List<Foo> foos)
{
var lastSelected = foos.Count;
for (var i = 0; i < foos.Count; i++)
{
var foo = foos[i];
if (foo.IsSelected)
{
for (var j = lastSelected; j < i; j++)
yield return foos[j];
lastSelected = i+1;
yield return foo;
}
}
}
List<Foo> sourceItems = new List<Foo>
{
new Foo(){ Name="First", IsSelected=false},
new Foo(){ Name="Second", IsSelected=true },
new Foo(){ Name="Third", IsSelected=false},
new Foo(){ Name="Fourth", IsSelected=true },
new Foo(){ Name="Fifth", IsSelected=false},
new Foo(){ Name="Sixth", IsSelected=true },
new Foo(){ Name="Seventh", IsSelected=true },
new Foo(){ Name="Eighth", IsSelected=false},
new Foo(){ Name="Ninth", IsSelected=false},
new Foo(){ Name="Tenth", IsSelected=false}
};
int startIndex = sourceItems.FindIndex(x => x.IsSelected);
int endIndex = sourceItems.FindLastIndex(x => x.IsSelected);
var items = new List<Foo>();
for (int i = startIndex; i <= endIndex; i++)
items.Add(sourceItems[i]);
public static class ListExtension
{
public static List<T> FindGroup<T>(this List<T> mylist, Predicate<T> pred)
{
var first = mylist.FindIndex(pred);
var last = mylist.FindLastIndex(pred);
last += 1; // to get the Last Element
return mylist.GetRange(first, last - first);
}
}
List<Foo> sourceItems = new List<Foo>{
new Foo(){ Name="First", IsSelected=false},
new Foo(){ Name="Second", IsSelected=true },
new Foo(){ Name="Third", IsSelected=false},
new Foo(){ Name="Fourth", IsSelected=true },
new Foo(){ Name="Fifth", IsSelected=false},
new Foo(){ Name="Sixth", IsSelected=true },
new Foo(){ Name="Seventh", IsSelected=true },
new Foo(){ Name="Eighth", IsSelected=false},
new Foo(){ Name="Ninth", IsSelected=false},
new Foo(){ Name="Tenth", IsSelected=false}
};
int startIndex = sourceItems.FindIndex(item => item.IsSelected);
int endIndex = sourceItems.FindLastIndex(item => item.IsSelected);
var result = sourceItems.Where((item, itemIndex) => itemIndex >= startIndex && itemIndex <= endIndex);
int endIndex = sourceItems.FindLastIndex(item => item.IsSelected);
var result = sourceItems
.TakeWhile((item, itemIndex) => itemIndex <= endIndex) // Take all before (and including) index of last match (must be before SkipWhile as that would change the index)
.SkipWhile(item => !item.IsSelected); // Skip up until the first item where IsSelected is true