How to get the index of an element in an IEnumerab

2020-01-24 03:04发布

I wrote this:

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

But I don't know if it already exists, does it?

12条回答
Animai°情兽
2楼-- · 2020-01-24 03:17

An alternative to finding the index after the fact is to wrap the Enumerable, somewhat similar to using the Linq GroupBy() method.

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

Which gives a use case of:

var items = new[] {1, 2, 3};
var indexedItems = items.ToIndexed();
foreach (var item in indexedItems)
{
    Console.WriteLine("items[{0}] = {1}", item.Index, item.Value);
}
查看更多
聊天终结者
3楼-- · 2020-01-24 03:20

I would implement it like this:

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;
    }
}
查看更多
Lonely孤独者°
4楼-- · 2020-01-24 03:22

I think the best option is to implement like this:

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

It will also not create the anonymous object

查看更多
对你真心纯属浪费
5楼-- · 2020-01-24 03:30

The whole point of getting things out as IEnumerable is so you can lazily iterate over the contents. As such, there isn't really a concept of an index. What you are doing really doesn't make a lot of sense for an IEnumerable. If you need something that supports access by index, put it in an actual list or collection.

查看更多
▲ chillily
6楼-- · 2020-01-24 03:31

I'd question the wisdom, but perhaps:

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

(using EqualityComparer<T>.Default to emulate != if needed) - but you need to watch to return -1 if not found... so perhaps just do it the long way

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;
}
查看更多
神经病院院长
7楼-- · 2020-01-24 03:31

The way I'm currently doing this is a bit shorter than those already suggested and as far as I can tell gives the desired result:

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

It's a bit clunky, but it does the job and is fairly concise.

查看更多
登录 后发表回答