¿Cómo obtener el índice de un elemento en un IEnumerable?

Yo escribí esto:

public static class EnumerableExtensions { public static int IndexOf(this IEnumerable obj, T value) { return obj .Select((a, i) => (a.Equals(value)) ? i : -1) .Max(); } public static int IndexOf(this IEnumerable obj, T value , IEqualityComparer comparer) { return obj .Select((a, i) => (comparer.Equals(a, value)) ? i : -1) .Max(); } } 

Pero no sé si ya existe, ¿verdad?

El objective de hacer las cosas como IEnumerable es para poder iterar perezosamente sobre los contenidos. Como tal, no existe realmente un concepto de índice. Lo que estás haciendo realmente no tiene mucho sentido para un IEnumerable. Si necesita algo que admita el acceso por índice, colóquelo en una lista o colección real.

Yo cuestionaría la sabiduría, pero quizás:

 source.TakeWhile(x => x != value).Count(); 

(utilizando EqualityComparer.Default para emular != si es necesario) – pero necesita mirar para devolver -1 si no se encuentra … así que quizás solo lo haga a lo largo del camino

 public static int IndexOf(this IEnumerable source, T value) { int index = 0; var comparer = EqualityComparer.Default; // or pass in as a parameter foreach (T item in source) { if (comparer.Equals(item, value)) return index; index++; } return -1; } 

Lo implementaría así:

 public static class EnumerableExtensions { public static int IndexOf(this IEnumerable obj, T value) { return obj.IndexOf(value, null); } public static int IndexOf(this IEnumerable obj, T value, IEqualityComparer comparer) { comparer = comparer ?? EqualityComparer.Default; var found = obj .Select((a, i) => new { a, i }) .FirstOrDefault(x => comparer.Equals(xa, value)); return found == null ? -1 : found.i; } } 

La forma en que estoy haciendo esto actualmente es un poco más corta que las ya sugeridas y, por lo que sé, da el resultado deseado:

  var index = haystack.ToList().IndexOf(needle); 

Es un poco torpe, pero cumple su función y es bastante conciso.

Creo que la mejor opción es implementar de esta manera:

 public static int IndexOf(this IEnumerable enumerable, T element, IEqualityComparer comparer = null) { int i = 0; comparer = comparer ?? EqualityComparer.Default; foreach (var currentElement in enumerable) { if (comparer.Equals(currentElement, element)) { return i; } i++; } return -1; } 

Tampoco creará el objeto anónimo

Un poco tarde en el juego, lo sé … pero esto es lo que hice recientemente. Es ligeramente diferente a la suya, pero le permite al progtwigdor dictar lo que la operación de igualdad necesita ser (predicado). Lo cual me parece muy útil cuando trato con diferentes tipos, ya que entonces tengo una forma genérica de hacerlo independientemente del tipo de objeto y integrado en el operador de igualdad.

También tiene una huella de memoria muy pequeña, y es muy, muy rápido / eficiente … si te importa eso.

En el peor, simplemente agregará esto a su lista de extensiones.

De todos modos … aquí está.

  public static int IndexOf(this IEnumerable source, Func predicate) { int retval = -1; var enumerator = source.GetEnumerator(); while (enumerator.MoveNext()) { retval += 1; if (predicate(enumerator.Current)) { IDisposable disposable = enumerator as System.IDisposable; if (disposable != null) disposable.Dispose(); return retval; } } IDisposable disposable = enumerator as System.IDisposable; if (disposable != null) disposable.Dispose(); return -1; } 

Espero que esto ayude a alguien.

Unos años más tarde, pero esto utiliza Linq, devuelve -1 si no se encuentra, no crea objetos adicionales, y debe cortocircuitar cuando se encuentre [en lugar de iterar sobre todo IEnumerable]:

 public static int IndexOf(this IEnumerable list, T item) { return list.Select((x, index) => EqualityComparer.Default.Equals(item, x) ? index : -1) .FirstOr(x => x != -1, -1); } 

Donde ‘FirstOr’ es:

 public static T FirstOr(this IEnumerable source, T alternate) { return source.DefaultIfEmpty(alternate) .First(); } public static T FirstOr(this IEnumerable source, Func predicate, T alternate) { return source.Where(predicate) .FirstOr(alternate); } 

La mejor forma de atrapar la posición es mediante FindIndex Esta función está disponible solo para List<>

Ejemplo

 int id = listMyObject.FindIndex(x => x.Id == 15); 

Si tiene enumerador o matriz, use de esta manera

 int id = myEnumerator.ToList().FindIndex(x => x.Id == 15); 

o

  int id = myArray.ToList().FindIndex(x => x.Id == 15); 

Una alternativa para encontrar el índice después del hecho es ajustar el Enumerable, algo similar al uso del método Linq GroupBy ().

 public static class IndexedEnumerable { public static IndexedEnumerable ToIndexed(this IEnumerable items) { return IndexedEnumerable.Create(items); } } public class IndexedEnumerable : IEnumerable.IndexedItem> { private readonly IEnumerable _items; public IndexedEnumerable(IEnumerable items) { _items = items; } public class IndexedItem { public IndexedItem(int index, T value) { Index = index; Value = value; } public T Value { get; private set; } public int Index { get; private set; } } public static IndexedEnumerable Create(IEnumerable items) { return new IndexedEnumerable(items.Select((item, index) => new IndexedItem(index, item))); } public IEnumerator GetEnumerator() { return _items.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } 

Lo que da un caso de uso de:

 var items = new[] {1, 2, 3}; var indexedItems = items.ToIndexed(); foreach (var item in indexedItems) { Console.WriteLine("items[{0}] = {1}", item.Index, item.Value); } 

Esto puede ser realmente genial con una extensión (que funciona como un proxy), por ejemplo:

 collection.SelectWithIndex(); // vs. collection.Select((item, index) => item); 

Que automaticamente asignará índices a la colección accesible a través de esta propiedad del Index .

Interfaz:

 public interface IIndexable { int Index { get; set; } } 

Extensión personalizada (probablemente la más útil para trabajar con EF y DbContext):

 public static class EnumerableXtensions { public static IEnumerable SelectWithIndex( this IEnumerable collection) where TModel : class, IIndexable { return collection.Select((item, index) => { item.Index = index; return item; }); } } public class SomeModelDTO : IIndexable { public Guid Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } public int Index { get; set; } } // In a method var items = from a in db.SomeTable where a.Id == someValue select new SomeModelDTO { Id = a.Id, Name = a.Name, Price = a.Price }; return items.SelectWithIndex() .OrderBy(m => m.Name) .Skip(pageStart) .Take(pageSize) .ToList(); 

Tropecé con esto hoy en una búsqueda de respuestas y pensé en agregar mi versión a la lista (sin juego de palabras). Emplea el operador condicional nulo de c # 6.0

 IEnumerable collection = GetTheCollection(); var index = collection .Select((item,idx) => new { Item = item, Index = idx }) .FirstOrDefault(_ => _.Item == itemToFind // or _.Item.Prop == something)?.Index ?? -1; 

He hecho algunas ‘carreras de caballos viejos’ (pruebas) y colecciones grandes (~ 100,000), en el peor de los casos, el elemento que quieres está al final, esto es 2 ToList().FindIndex() más rápido que hacer ToList().FindIndex() . Si el artículo que desea está en el medio, es ~ 4 veces más rápido.

Para colecciones más pequeñas (~ 10,000) parece ser solo un poco más rápido

Heres cómo lo probé https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e