C# 如何获取 IEnumerable 中元素的索引?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1290603/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get the index of an element in an IEnumerable?
提问by Jader Dias
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?
但我不知道它是否已经存在,是吗?
采纳答案by Scott Dorman
The whole point of getting things out as IEnumerable is so you can lazily iterate over the contents. As such, there isn't reallya 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.
将事物作为 IEnumerable 的全部意义在于,您可以懒惰地迭代内容。因此,没有真正的指数的概念。对于 IEnumerable,您正在做的事情确实没有多大意义。如果您需要支持按索引访问的内容,请将其放入实际列表或集合中。
回答by Marc Gravell
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
(如果需要,EqualityComparer<T>.Default
用于模拟!=
)-但是如果找不到,您需要注意返回 -1 ......所以也许只是做很长的路
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;
}
回答by dahlbyk
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;
}
}
回答by Axente Adrian
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
它也不会创建匿名对象
回答by Joshka
An alternative to finding the index after the fact is to wrap the Enumerable, somewhat similar to using the Linq GroupBy() method.
事后查找索引的另一种方法是包装 Enumerable,有点类似于使用 Linq GroupBy() 方法。
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);
}
回答by Mark Watts
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.
它有点笨重,但它可以完成工作并且相当简洁。
回答by MaxOvrdrv
A bit late in the game, i know... but this is what i recently did. It is slightly different than yours, but allows the programmer to dictate what the equality operation needs to be (predicate). Which i find very useful when dealing with different types, since i then have a generic way of doing it regardless of object type and <T>
built in equality operator.
我知道比赛有点晚了……但这就是我最近所做的。它与您的略有不同,但允许程序员决定相等运算需要是什么(谓词)。我发现在处理不同类型时非常有用,因为无论对象类型和<T>
内置相等运算符如何,我都有一个通用的方法来做它。
It also has a very very small memory footprint, and is very, very fast/efficient... if you care about that.
它还具有非常非常小的内存占用,并且非常非常快速/高效......如果你关心的话。
At worse, you'll just add this to your list of extensions.
更糟糕的是,您只需将其添加到扩展列表中即可。
Anyway... here it is.
无论如何……它来了。
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
int retval = -1;
var enumerator = source.GetEnumerator();
while (enumerator.MoveNext())
{
retval += 1;
if (predicate(enumerator.Current))
{
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
return retval;
}
}
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
return -1;
}
Hopefully this helps someone.
希望这有助于某人。
回答by Greg
A few years later, but this uses Linq, returns -1 if not found, doesn't create extra objects, and shouldshort-circuit when found [as opposed to iterating over the entire IEnumerable]:
几年后,但它使用 Linq,如果未找到则返回 -1,不会创建额外的对象,并且在找到时应该短路 [而不是遍历整个 IEnumerable]:
public static int IndexOf<T>(this IEnumerable<T> list, T item)
{
return list.Select((x, index) => EqualityComparer<T>.Default.Equals(item, x)
? index
: -1)
.FirstOr(x => x != -1, -1);
}
Where 'FirstOr' is:
'FirstOr' 是:
public static T FirstOr<T>(this IEnumerable<T> source, T alternate)
{
return source.DefaultIfEmpty(alternate)
.First();
}
public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, T alternate)
{
return source.Where(predicate)
.FirstOr(alternate);
}
回答by daniele3004
The best way to catch the position is by FindIndex
This function is available only for List<>
捕捉位置的最佳方法是通过FindIndex
此功能仅适用于List<>
Example
例子
int id = listMyObject.FindIndex(x => x.Id == 15);
If you have enumerator or array use this way
如果您有枚举器或数组,请使用这种方式
int id = myEnumerator.ToList().FindIndex(x => x.Id == 15);
or
或者
int id = myArray.ToList().FindIndex(x => x.Id == 15);
回答by Yom S.
This can get really cool with an extension (functioning as a proxy), for example:
这可以通过扩展(充当代理)变得非常酷,例如:
collection.SelectWithIndex();
// vs.
collection.Select((item, index) => item);
Which will automagically assign indexes to the collection accessible via this Index
property.
这将自动为可通过此Index
属性访问的集合分配索引。
Interface:
界面:
public interface IIndexable
{
int Index { get; set; }
}
Custom extension (probably most useful for working with EF and DbContext):
自定义扩展(可能对使用 EF 和 DbContext 最有用):
public static class EnumerableXtensions
{
public static IEnumerable<TModel> SelectWithIndex<TModel>(
this IEnumerable<TModel> collection) where TModel : class, IIndexable
{
return collection.Select((item, index) =>
{
item.Index = index;
return item;
});
}
}
public class SomeModelDTO : IIndexable
{
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Index { get; set; }
}
// In a method
var items = from a in db.SomeTable
where a.Id == someValue
select new SomeModelDTO
{
Id = a.Id,
Name = a.Name,
Price = a.Price
};
return items.SelectWithIndex()
.OrderBy(m => m.Name)
.Skip(pageStart)
.Take(pageSize)
.ToList();