给定像这样的数据源:
var c = new Car[] { new Car{ Color="Blue", Price=28000}, new Car{ Color="Red", Price=54000}, new Car{ Color="Pink", Price=9999}, // .. };
如何用LINQ 找到满足一定条件的第一辆车的索引?
编辑:
我可以想到这样的东西,但它看起来很糟糕:
int firstItem = someItems.Select((item, index) => new { ItemName = item.Color, Position = index }).Where(i => i.ItemName == "purple") .First() .Position;
用一个普通的循环解决这个问题会是最好的吗?
myCars.Select((v, i) => new {car = v, index = i}).First(myCondition).index;
或稍短
myCars.Select((car, index) => new {car, index}).First(myCondition).index;
简单地说:
int index = List.FindIndex(your condition);
例如
int index = cars.FindIndex(c => c.ID == 150);
An IEnumerable
不是有序集.
虽然大多数IEnumerables是有序的,但有些(例如Dictionary
或HashSet
)不是.
因此,LINQ没有IndexOf
方法.
但是,你可以自己写一个:
///Finds the index of the first item matching an expression in an enumerable. ///The enumerable to search. ///The expression to test the items against. ///The index of the first matching item, or -1 if no items match. public static int FindIndex(this IEnumerable items, Func predicate) { if (items == null) throw new ArgumentNullException("items"); if (predicate == null) throw new ArgumentNullException("predicate"); int retVal = 0; foreach (var item in items) { if (predicate(item)) return retVal; retVal++; } return -1; } /// Finds the index of the first occurrence of an item in an enumerable. ///The enumerable to search. ///The item to find. ///The index of the first matching item, or -1 if the item was not found. public static int IndexOf(this IEnumerable items, T item) { return items.FindIndex(i => EqualityComparer .Default.Equals(item, i)); }
myCars.TakeWhile(car => !myCondition(car)).Count();
有用!想一想.第一个匹配项的索引等于它之前的(不匹配)项的数量.
讲故事的时间我也不喜欢你在问题中提出的可怕的标准解决方案.就像接受的答案一样,我选择了一个普通的旧循环,尽管稍作修改:
public static int FindIndex(this IEnumerable items, Predicate predicate) { int index = 0; foreach (var item in items) { if (predicate(item)) break; index++; } return index; }
请注意,它将返回项目数,而不是-1
在没有匹配项时.但是现在让我们忽略这个小麻烦.事实上,可怕的标准解决方案在这种情况下崩溃,我考虑返回一个超出界限的索引.
现在发生的事情是ReSharper告诉我Loop可以转换成LINQ表达式.虽然大多数时候该功能会降低可读性,但这次结果令人敬畏.所以感谢JetBrains.
分析
简洁
可与其他LINQ组合使用
避免new
匿名对象
仅评估可枚举,直到谓词第一次匹配
因此我认为它在时间和空间上是最佳的,同时保持可读性.
起初不太明显
-1
没有匹配时不返回
当然,您始终可以将其隐藏在扩展方法后面.当没有匹配时,最好的做法取决于上下文.
我会在这里做出贡献......为什么?只是因为:p它是一个基于Any LINQ扩展和委托的不同实现.这里是:
public static class Extensions { public static int IndexOf( this IEnumerable list, Predicate condition) { int i = -1; return list.Any(x => { i++; return condition(x); }) ? i : -1; } } void Main() { TestGetsFirstItem(); TestGetsLastItem(); TestGetsMinusOneOnNotFound(); TestGetsMiddleItem(); TestGetsMinusOneOnEmptyList(); } void TestGetsFirstItem() { // Arrange var list = new string[] { "a", "b", "c", "d" }; // Act int index = list.IndexOf(item => item.Equals("a")); // Assert if(index != 0) { throw new Exception("Index should be 0 but is: " + index); } "Test Successful".Dump(); } void TestGetsLastItem() { // Arrange var list = new string[] { "a", "b", "c", "d" }; // Act int index = list.IndexOf(item => item.Equals("d")); // Assert if(index != 3) { throw new Exception("Index should be 3 but is: " + index); } "Test Successful".Dump(); } void TestGetsMinusOneOnNotFound() { // Arrange var list = new string[] { "a", "b", "c", "d" }; // Act int index = list.IndexOf(item => item.Equals("e")); // Assert if(index != -1) { throw new Exception("Index should be -1 but is: " + index); } "Test Successful".Dump(); } void TestGetsMinusOneOnEmptyList() { // Arrange var list = new string[] { }; // Act int index = list.IndexOf(item => item.Equals("e")); // Assert if(index != -1) { throw new Exception("Index should be -1 but is: " + index); } "Test Successful".Dump(); } void TestGetsMiddleItem() { // Arrange var list = new string[] { "a", "b", "c", "d", "e" }; // Act int index = list.IndexOf(item => item.Equals("c")); // Assert if(index != 2) { throw new Exception("Index should be 2 but is: " + index); } "Test Successful".Dump(); }