Neith
Neith

Reputation: 17

Get index of a specific item from list containing possible duplicates

okay, I have a list of class objects like so:

List<fruit> lst = new List<fruit>();
    lst.Add(orange);
    lst.Add(apple);
    lst.Add(grape);
    lst.Add(grape);
    lst.Add(orange);
    lst.Add(pear);
    lst.Add(apple);

I want to be able to ask the list something like GetIndex("orange", 2) and have it return (in this case) the index # for the second instance of object in question (position 4).

This list will be dynamically filled, and it may not even have an orange to begin with. If it does, I want the instance number of the second parameter. So I can get the second orange, or get fifth mango, etc.

list.IndexOf(orange) returns the first instance of any duplicates, so I need something else.

Any ideas?

PS: I failed to mention that the first param will be a string!

Upvotes: 1

Views: 10379

Answers (6)

mattmc3
mattmc3

Reputation: 18345

An extension of dtb's answer to account for when you don't find the value you're looking for:

public int GetIndex<T>(IEnumerable<T> list, T item, int itemNum) {
    // result is a nullable int containing the index
    var result = list.Select((x, i) => new { x, i })
                     .Where(t => item.Equals(t.x))
                     .Skip(itemNum - 1)
                     .Select(t => (int?)t.i)
                     .FirstOrDefault();
    // return -1 when item was not found
    return (result.HasValue ? result.Value : -1);
}

Upvotes: 0

Petar Ivanov
Petar Ivanov

Reputation: 93050

        int index = lst.IndexOf(orange);
        if (index >= 0)
        {
            index = lst.IndexOf(orange, index + 1);
        }

or to make it generic you can use some LINQ:

    static int GetIndex(IEnumerable<Fruit> li, Fruit ob, int k)
    {
        var tmp = li.Select((it, i) => new Tuple<int, Fruit>(i, it)).Where(tup => tup.Item2 == ob).Skip(k - 1).FirstOrDefault();
        if (tmp == null)
            return -1;
        else 
            return tmp.Item1;
    }

Then call GetIndex(lst, orange, 2)

Upvotes: 2

Nahydrin
Nahydrin

Reputation: 13517

Here's a generic extension search I just wrote up:

public static class ListExtension
{
    public static int GetIndex<T>(this List<T> entity, T what, int find)
    {
        int found = 0;
        int index = -1;

        while ((index = entity.IndexOf(what, (index + 1))) != -1)
        {
            found++;

            if (found == find)
                break;
        }

        return (index);
    }
}

All you have to do is call it like so:

int index = lst.GetIndex(apple, 2);

If the item you are looking for isn't found, it returns -1.

Upvotes: 2

MrFox
MrFox

Reputation: 5126

You can extend classes with your own method for that class, sadly this is not possible for generic classes, so instead you give the type with the method.

public static class ListExtension
{
    public static int GetIndex<T>(this List<T> list, T value, int skipMatches = 1)
    {
        for (int i = 0; i < list.Count; i++)
            if (list[i].Equals(value))
            {
                skipMatches--;
                if (skipMatches == 0)
                    return i;
            }
        return -1;
    }
}

List<int> list = new List<int>();
list.Add(3);
list.Add(4);
list.Add(5);
list.Add(4);
int secondFour = (int)list.GetIndex(4, 2);

Upvotes: 1

svick
svick

Reputation: 244928

public static int GetIndex<T>(this IEnumerable<T> lst, T obj, int index)
{
    return lst.Select((o, i) => new { o, i })
              .Where(x => x.o.Equals(obj))
              .ElementAt(index - 1)
              .i;
}

Although it's kind of strange that index starts at 1, but the result starts at 0.

Upvotes: 3

dtb
dtb

Reputation: 217341

var result = list.Select((x, i) => new { x, i })
                  .Where(t => t.x == fruit)
                  .Skip(k - 1)
                  .Select(t => t.i)
                  .First();

Upvotes: 1

Related Questions