Question

J'ai écrit ceci:

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

Mais je ne sais pas si elle existe déjà, le fait?

Était-ce utile?

La solution

Le point entier de faire avancer les choses comme IEnumerable est si vous pouvez paresseusement itérer sur le contenu. En tant que tel, il n'y a pas vraiment un concept d'un indice. Ce que vous faites n'a vraiment pas beaucoup de sens pour un IEnumerable. Si vous avez besoin quelque chose qui prend en charge l'accès par index, le mettre dans une liste réelle ou d'une collection.

Autres conseils

Je remets en question la sagesse, mais peut-être:

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

(en utilisant EqualityComparer<T>.Default à imiter != si nécessaire) - mais vous devez regarder pour retourner -1 si elle est introuvable ... alors peut-être tout simplement le faire le long chemin

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

Je mettre en œuvre comme ceci:

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

La façon dont je fais actuellement c'est un peu plus courtes que celles déjà proposées et pour autant que je peux dire donne le résultat souhaité:

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

Il est un peu maladroit, mais il fait le travail et est assez concis.

Je pense que la meilleure option est de mettre en œuvre comme ceci:

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

Il sera également ne pas créer l'objet anonyme

La meilleure façon de prendre la position est par FindIndex Cette fonction est disponible uniquement pour List<>

Exemple

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

Si vous avez recenseur ou un tableau utilisent cette façon

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

ou

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

Un peu en retard dans le jeu, je sais ... mais ce que je l'ai fait récemment. Il est légèrement différente de la vôtre, mais permet au programmeur de dicter ce que l'opération de l'égalité doit être (prédicat). Ce que je trouve très utile pour traiter différents types, puisque j'ai alors un moyen générique de le faire quel que soit le type d'objet et <T> construit opérateur d'égalité.

Il a également une très très faible empreinte mémoire, et est très, très rapide / efficace ... si vous tenez à ce sujet.

Au pire, il vous suffit de l'ajouter à votre liste d'extensions.

Quoi qu'il en soit ... ici il 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;
 }

Espérons que cela aide quelqu'un.

Quelques années plus tard, mais il utilise Linq, renvoie -1 si elle est introuvable, ne crée pas d'objets supplémentaires, et devraient court-circuit lorsque trouvé [par opposition à itérer sur toute 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);
}

Où FirstOr 'est:

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

Une alternative à la recherche de l'index après le fait est d'envelopper le Enumerable, quelque peu similaire à l'aide de la méthode 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();
    }
}

Ce qui donne un cas d'utilisation 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);
}

a trébuché à travers aujourd'hui dans une recherche de réponses et je pensais que j'ajouter ma version à la liste (sans jeu de mots). Il utlises l'opérateur conditionnel nulle de 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;

Je l'ai fait une « course des vieux chevaux » (tests) et pour les grandes collections (~ 100 000), le pire des cas, cet élément que vous voulez est à la fin, c'est 2x plus rapide que de faire ToList().FindIndex(). Si l'élément que vous voulez est au milieu de son ~ 4x plus rapide.

Pour les petites collections (~ 10 000), il semble que marginalement plus rapide

Voici comment je l'ai testé https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e

En utilisant la réponse de » @Marc Gravell, j'ai trouvé un moyen d'utiliser la méthode suivante:

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

afin d'obtenir -1 lorsque l'élément ne peut pas être trouvé:

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

Je suppose que cette façon pourrait être à la fois le plus rapide et le plus simple. Cependant, je ne l'ai pas encore testé les performances.

Cela peut être vraiment cool avec une extension (fonctionnant comme un proxy), par exemple:

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

Ce qui sera magiquement assigner un index à la collecte accessible via cette propriété Index.

Interface:

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

extension personnalisée (probablement le plus utile pour travailler avec EF et 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();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top