Как получить индекс с помощью LINQ? [дубликат]
этот вопрос уже есть ответ здесь:
- получить список положение элемента в 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
когда нет матча. Но давайте игнорировать это раздражает сейчас. На самом деле ужасное стандартное решение сбой в этом случае и я рассматриваю возможность возврата индекса, который выходит за рамки superior.
что происходит сейчас ReSharper говорит мне цикл может быть преобразован в LINQ-expression. Хотя в большинстве случаев функция ухудшает читаемость, на этот раз результат был впечатляющим. Так что спасибо В JetBrains.
анализ
плюсы
- Краткий
- Combinable с другим LINQ
- избежать
new
Инг анонимные объекты - оценивает только перечисляемое до тех пор, пока предикат не совпадет в первый раз
поэтому я считаю его оптимальным во времени и пространстве, оставаясь читабельным.
минусы
- сначала не совсем очевидно
- тут не возвращайся
-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;
}