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

Open
rjperes opened this issue Nov 21, 2024 · 8 comments
Open

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

rjperes opened this issue Nov 21, 2024 · 8 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Linq untriaged New issue has not been triaged by the area owner

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

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 untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

5 participants