简体   繁体   中英

Query IEnumerable as IEnumerable<Type>

I have a problem I need to solve efficiently.

I require the index of an element in an IEnumerable source, one way I could do this is with the following

    var items = source.Cast<ObjectType>().Where(obj => obj.Start == forDate);

This would give me an IEnumerable of all the items that match the predicate.

   if(items != null && items.Any()){
       // I now need the ordinal from the original list
       return source.IndexOf(items[0]);
   }

However, the list could be vast and the operation will be carried out many times. I believe this is inefficient and there must be a better way to do this.

I would be grateful if anyone can point me in the correct direction.

Sometimes, it's good to forget about Linq and go back to basics:

int index = 0;

foeach (ObjectType element in source)
{
    if (element.Start == forDate)
    {
        return index;
    }

    index++;
}

// No element found

Using Linq, you can take the index of each object before filtering them:

source
    .Cast<ObjectType>()
    .Select((obj, i) => new { Obj = obj, I = i })
    .Where(x => x.Obj.Start == forDate)
    .Select(x => x.I)
    .FirstOrDefault();

However, this is not really efficient, the following will do the same without allocations:

int i = 0;
foreach (ObjectType obj in source)
{
    if (obj.Start == forDate)
    {
        return i;
    }
    i++;
}

Your second code sample was invalid: since items is an IEnumerable , you cannot call items[0] . You can use First() . Anyway:

var items = source.Cast<ObjectType>()
.Select((item, index) => new KeyValuePair<int, ObjectType>(index, item))
.Where(obj => obj.Value.Start == forDate);

and then:

if (items != null && items.Any()) {
    return items.First().Key;
}

If you need to do this multiple times I would create a lookup for the indices.

ILookup<DateTime, int> lookup =
    source
        .Cast<ObjectType>()
        .Select((e, i) => new { e, i })
        .ToLookup(x => x.e.Start, x => x.i);

Now given a forDate you can do this:

IEnumerable<int> indices = lookup[forDate];

Since the lookup is basically like a dictionary that returns multiple values you get the results instantly. So repeating this for multiple values is super fast.

And since this returns IEnumerable<int> you know when there are duplicate values within the source list. If you only need the first one then just do a .First() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM