index c# .net linq ienumerable indexof

c# - index - ¿Cómo obtener el índice de un elemento en un IEnumerable?



c# ienumerable access element (11)

Yo escribí esto:

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

Pero no sé si ya existe, ¿verdad?


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

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

Tampoco creará el objeto anónimo


El objetivo 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.


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<TModel> SelectWithIndex<TModel>( this IEnumerable<TModel> 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();


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.


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);


Lo implementaría así:

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


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<Item> 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


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 programador 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 <T> 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<T>(this IEnumerable<T> source, Func<T, bool> 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.


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<T> ToIndexed<T>(this IEnumerable<T> items) { return IndexedEnumerable<T>.Create(items); } } public class IndexedEnumerable<T> : IEnumerable<IndexedEnumerable<T>.IndexedItem> { private readonly IEnumerable<IndexedItem> _items; public IndexedEnumerable(IEnumerable<IndexedItem> 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<T> Create(IEnumerable<T> items) { return new IndexedEnumerable<T>(items.Select((item, index) => new IndexedItem(index, item))); } public IEnumerator<IndexedItem> 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); }


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<T>(this IEnumerable<T> list, T item) { return list.Select((x, index) => EqualityComparer<T>.Default.Equals(item, x) ? index : -1) .FirstOr(x => x != -1, -1); }

Donde ''FirstOr'' es:

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


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

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

(utilizando EqualityComparer<T>.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<T>(this IEnumerable<T> source, T value) { int index = 0; var comparer = EqualityComparer<T>.Default; // or pass in as a parameter foreach (T item in source) { if (comparer.Equals(item, value)) return index; index++; } return -1; }