Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Proposal]: IndexOf extension method on IEnumerable<T> #110045

Closed
rjperes opened this issue Nov 21, 2024 · 13 comments
Closed

[API Proposal]: IndexOf extension method on IEnumerable<T> #110045

rjperes opened this issue Nov 21, 2024 · 13 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Linq

Comments

@rjperes
Copy link

rjperes commented Nov 21, 2024

Background and motivation

As the counterpart to ElementAt(), and Index(), I suggest the creation of an IndexOf() method, which would return the index of the passed element on a list, or -1 if the element is not found. This could even be implemented in LINQ to databases, as there are ways to obtain the index of a row.

API Proposal

namespace System.Collections.Generic;

public static class EnumerableExtensions
{
    public static int IndexOf<T>(this IEnumerable<T> source, T element)
    {
        ArgumentNullException.ThrowIfNull(source);
        //just an idea, but can be made simpler, without needing to traverse the source twice 
        //if source is List<T>, we can just use FindIndex
        return source.Index().Where(elm => elm.Item.Equals(element)).Select(x => x.Index).FirstOrDefault(-1);
    }
}

API Usage

string[] colours = ["red", "green", "blue"];
var greenIndex = colours.IndexOf("green");

Alternative Designs

No response

Risks

No breaking changes, as it's a new method.

@rjperes rjperes added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Nov 21, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Nov 21, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-linq
See info in area-owners.md if you want to be subscribed.

@stephentoub
Copy link
Member

stephentoub commented Nov 21, 2024

string[] colours = ["red", "green", "blue"];
var greenIndex = colours.IndexOf("green");

That already works today if you set <LangVersion>preview</LangVersion> in your csproj, as MemoryExtensions.IndexOf then automatically applies to arrays. Plus IList<T> already has an IndexOf. So this would really only be about non-indexible collections. I'd be concerned about exposing something like this for non-indexible enumerables, if for no other reason than there's no guarantee the index will be correct for a second enumeration.

@rjperes
Copy link
Author

rjperes commented Nov 21, 2024

Right, and how is that different from ElementAt, for non-indexable collections, which already exists?
As for the method you mentioned, it will work for strings, but not in general for any T.

@stephentoub
Copy link
Member

As for the method you mentioned, it will work for strings, but not in general for any T.

It'll work for any T : IEquatable<T>, and #28934 will work for all other T.

and how is that different from ElementAt, for non-indexable collections, which already exists?

ElementAt is problematic, for a variety of reasons, including that the data could change on subsequent enumeration and including that it can easily lead to unexpected O(N^2) evaluation. I don't think we should be doubling-down on it.

@huoyaoyuan
Copy link
Member

and how is that different from ElementAt, for non-indexable collections, which already exists?

ElementAt is problematic, for a variety of reasons, including that the data could change on subsequent enumeration and including that it can easily lead to unexpected O(N^2) evaluation. I don't think we should be doubling-down on it.

ElementAt can be effectively considered as Skip(n).Take(1), which is a one-shot usage of the enumerable in case you do need only one element.

For IndexOf, the index is much less usable without consuming the enumerable again.

Plus IList<T> already has an IndexOf. So this would really only be about non-indexible collections.

How about IReadOnlyList<T>? It doesn't provide lookup because of covariance.

@eiriktsarpalis
Copy link
Member

Agree that we shouldn't be exposing this due to the index not being practical to use with arbitrary enumerables.

@julealgon
Copy link

Agree that we shouldn't be exposing this due to the index not being practical to use with arbitrary enumerables.

@eiriktsarpalis what if you just want to know the position at that point in time for some other purpose, and you don't intend to try to use it to index back into the enumerable?

@eiriktsarpalis
Copy link
Member

Can you share an example where this might be the case? Is it common enough to warrant inclusion in System.Linq and not be a helper when necessary?

@julealgon
Copy link

Can you share an example where this might be the case?

To be honest I can't of the top of my head, no. I was playing devil's advocate on this one.

Is it common enough to warrant inclusion in System.Linq and not be a helper when necessary?

Fair point. It is probably not common enough. I really can't say as I personally never had the need for it.

My argument was solely on the reasoning that "because you can't reliably index back into the IEnumerable with the value, it is not a good idea". I still think that particular argument is somewhat weak as it assumes that the only ever need for the index is to index back into the original collection.

@rjperes
Copy link
Author

rjperes commented Nov 22, 2024

“ My argument was solely on the reasoning that "because you can't reliably index back into the IEnumerable with the value, it is not a good idea"

Of course, this is an argument against ElementAt.
The implementation I proposed could of course be different, but this would be an O(N), and I think would be a good companion to Index and ElementAt, which would work for any kind of T, not just those that implement some interface. Also, a similar method over IQueryable could also come in handy. But if you guys think that it’s inappropriate, then just close it.

@eiriktsarpalis
Copy link
Member

I still think that particular argument is somewhat weak as it assumes that the only ever need for the index is to index back into the original collection.

That may be true, but at the same we wouldn't want to consider an API unless there is evidence of it being impactful.

@rjperes
Copy link
Author

rjperes commented Nov 22, 2024

Fair enough. Like all LINQ methods, this can be easily achieved, so if you don’t think this in relevant, then by all means close it.

@eiriktsarpalis
Copy link
Member

Sounds good. We can always revisit the proposal if we have more evidence of this being impactful enough for inclusion.

@eiriktsarpalis eiriktsarpalis closed this as not planned Won't fix, can't repro, duplicate, stale Nov 22, 2024
@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Linq
Projects
None yet
Development

No branches or pull requests

5 participants