Как получить индекс с помощью LINQ? [дубликат]
этот вопрос уже есть ответ здесь:
- Получить List положение элемента в c# с помощью LINQ 9 ответов
- Как получить индекс элемента в IEnumerable? 11 ответов
приведен источник данных, как что:
var c = new Car[]
{
new Car{ Color="Blue", Price=28000},
new Car{ Color="Red", Price=54000},
new Car{ Color="Pink", Price=9999},
// ..
};
Как найти индекс первого автомобиля, удовлетворяющего определенному условию с LINQ?
EDIT:
я мог бы придумать что-то вроде этого, но это выглядит ужасно:
int firstItem = someItems.Select((item, index) => new
{
ItemName = item.Color,
Position = index
}).Where(i => i.ItemName == "purple")
.First()
.Position;
будет ли лучше решить эту проблему с помощью простого старого цикла?
7 ответов:
An
IEnumerable
не является упорядоченным набором.
Хотя большинство IEnumerables упорядочены, некоторые (например,Dictionary
илиHashSet
) не являются.таким образом, LINQ, которая не имеет
IndexOf
метод.однако, вы можете написать свое:
///<summary>Finds the index of the first item matching an expression in an enumerable.</summary> ///<param name="items">The enumerable to search.</param> ///<param name="predicate">The expression to test the items against.</param> ///<returns>The index of the first matching item, or -1 if no items match.</returns> public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> 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; } ///<summary>Finds the index of the first occurrence of an item in an enumerable.</summary> ///<param name="items">The enumerable to search.</param> ///<param name="item">The item to find.</param> ///<returns>The index of the first matching item, or -1 if the item was not found.</returns> public static int IndexOf<T>(this IEnumerable<T> items, T item) { return items.FindIndex(i => EqualityComparer<T>.Default.Equals(item, i)); }
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);
myCars.TakeWhile(car => !myCondition(car)).Count();
это работает! Думать об этом. Индекс первого совпадающего элемента равен числу (не совпадающих) элементов перед ним.
история времени
я тоже не люблю ужасное стандартное решение вы уже предлагали в своем вопросе. Как и принятый ответ, я пошел на простой старый цикл, хотя и с небольшой модификацией:
public static int FindIndex<T>(this IEnumerable<T> items, Predicate<T> predicate) { int index = 0; foreach (var item in items) { if (predicate(item)) break; index++; } return index; }
обратите внимание, что он будет возвращать количество элементов вместо
-1
когда нет матча. Но давайте не обращайте внимания на это незначительное раздражение. На самом деле ужасное стандартное решение сбой в этом случае и я считаю, что возвращаю индекс, который превосходит границы.то, что происходит сейчас, говорит мне Решарпер цикл может быть преобразован в LINQ-выражение. Хотя большую часть времени функция ухудшает читаемость, на этот раз результат был впечатляющим. Так что престижность В JetBrains.
анализ
плюсы
- Краткий
- комбинируется с другими LINQ
- избежать
new
ing анонимные объекты- вычисляет только перечислимое, пока предикат не совпадет в первый раз
поэтому я считаю его оптимальным во времени и пространстве, оставаясь читабельным.
минусы
- не совсем очевидно на первый
- тут не вернуть
-1
когда нет матчаконечно, вы всегда можете скрыть его за методом расширения. А что делать лучше всего, когда нет совпадения, сильно зависит от контекста.
я внесу свой вклад здесь... зачем? просто потому, что: p-это другая реализация, основанная на любом расширении LINQ и делегате. Вот это:
public static class Extensions { public static int IndexOf<T>( this IEnumerable<T> list, Predicate<T> 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(); }
вот небольшое расширение, которое я только что собрал.
public static class PositionsExtension { public static Int32 Position<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { return Positions<TSource>(source, predicate).FirstOrDefault(); } public static IEnumerable<Int32> Positions<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (typeof(TSource) is IDictionary) { throw new Exception("Dictionaries aren't supported"); } if (source == null) { throw new ArgumentOutOfRangeException("source is null"); } if (predicate == null) { throw new ArgumentOutOfRangeException("predicate is null"); } var found = source.Where(predicate).First(); var query = source.Select((item, index) => new { Found = ReferenceEquals(item, found), Index = index }).Where( it => it.Found).Select( it => it.Index); return query; } }
затем вы можете назвать это так.
IEnumerable<Int32> indicesWhereConditionIsMet = ListItems.Positions(item => item == this); Int32 firstWelcomeMessage ListItems.Position(msg => msg.WelcomeMessage.Contains("Hello"));
вот реализация ответа с самым высоким голосованием, который возвращает -1, когда элемент не найден:
public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> predicate) { var itemsWithIndices = items.Select((item, index) => new { Item = item, Index = index }); var matchingIndices = from itemWithIndex in itemsWithIndices where predicate(itemWithIndex.Item) select (int?)itemWithIndex.Index; return matchingIndices.FirstOrDefault() ?? -1; }