Domanda

ho scritto questo:

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

Ma non so se esiste già, vero?

È stato utile?

Soluzione

L'intero punto di fare le cose come IEnumerable è così si può pigramente scorrere i contenuti. In quanto tale, non è davvero un concetto di un indice. Quello che state facendo in realtà non ha molto senso per un IEnumerable. Se avete bisogno di qualcosa che supporta l'accesso per indice, metterlo in un elenco reale o di raccolta.

Altri suggerimenti

Mi piacerebbe in discussione la saggezza, ma forse:

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

(usando EqualityComparer<T>.Default per emulare != se necessario) - ma è necessario guardare per tornare -1 se non lo trova ... così forse basta farlo la strada più lunga

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

Vorrei implementare in questo modo:

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

Il modo in cui sto facendo attualmente questo è un po 'più brevi di quelli già suggerito e, per quanto posso dire dà il risultato desiderato:

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

E 'un po' goffo, ma fa il lavoro ed è abbastanza conciso.

Credo che la soluzione migliore è quella di implementare in questo modo:

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

Sarà anche non creare l'oggetto anonimo

Il modo migliore per catturare la posizione è di gran FindIndex Questa funzione è disponibile solo per List<>

Esempio

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

Se avete enumeratore o array usano in questo modo

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

o

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

Un po 'tardi nel gioco, lo so ... ma questo è quello che ho recentemente fatto. E 'un po' diversa dalla vostra, ma permette al programmatore di dettare ciò che l'operazione di uguaglianza deve essere (predicato). Che trovo molto utile quando si tratta di diversi tipi, dal momento che ho poi ho un modo generico di farlo a prescindere dal tipo di oggetto e <T> costruito in operatore di uguaglianza.

Ha anche una molto molto piccola orma di memoria, ed è molto, molto veloce / efficiente ... se vi preoccupate per questo.

Al peggio, ti basta aggiungere questo alla tua lista di estensioni.

Comunque ... eccolo.

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

Speriamo che questo aiuta qualcuno.

Alcuni anni più tardi, ma questo utilizza LINQ, restituisce -1 se non trovato, non crea oggetti extra, e dovrebbe corto circuito quando ha trovato [in contrasto con l'iterazione sull'intero 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);
}

Dove 'FirstOr' è:

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

Un'alternativa di trovare l'indice dopo il fatto è quello di avvolgere l'Enumerable, in qualche modo simile a utilizzare il metodo 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();
    }
}

che dà un caso d'uso di:

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

imbattuti in questo oggi, in una ricerca di risposte e ho pensato di aggiungere la mia versione alla lista (no pun intended). E utlises l'operatore condizionale nulla di c # 6.0

IEnumerable<Item> collection = GetTheCollection();

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

Ho fatto un po 'di corsa dei vecchi cavalli (testing) e per le grandi collezioni (~ 100.000), peggiore delle ipotesi che elemento che si desidera è alla fine, questo è 2 più veloce che fare ToList().FindIndex(). Se l'elemento desiderato si trova nel mezzo il suo ~ 4x più veloce.

Per le collezioni più piccole (~ 10.000) sembra essere solo marginalmente più veloce

Ecco come ho provato https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e

Utilizzando risposta @ Marc Gravell 's, ho trovato un modo per utilizzare il seguente metodo:

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

al fine di ottenere -1 quando l'elemento non può essere trovato:

internal static class Utils
{

    public static int IndexOf<T>(this IEnumerable<T> enumerable, T item) => enumerable.IndexOf(item, EqualityComparer<T>.Default);

    public static int IndexOf<T>(this IEnumerable<T> enumerable, T item, EqualityComparer<T> comparer)
    {
        int index = enumerable.TakeWhile(x => comparer.Equals(x, item)).Count();
        return index == enumerable.Count() ? -1 : index;
    }
}

Credo che in questo modo potrebbe essere sia il più veloce e più semplice. Tuttavia, non ho ancora testato le prestazioni.

Questo può ottenere davvero cool con l'estensione (che funge da proxy), ad esempio:

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

Quali saranno automagicamente assegnare indici per la collezione accessibile tramite questa proprietà Index.

Interfaccia:

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

estensione personalizzata (probabilmente più utile per lavorare con EF e 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();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top