我写了这个:
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();
}
}
但我不知道它是否已经存在,是吗?
答案 0 :(得分:106)
我会质疑智慧,但也许:
source.TakeWhile(x => x != value).Count();
(如果需要,使用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;
}
答案 1 :(得分:46)
因为IEnumerable而把事情搞得一团糟,所以你可以懒洋洋地迭代内容。因此,确实没有索引的概念。你正在做的事情对于IEnumerable来说真的没有多大意义。如果您需要支持索引访问的内容,请将其放在实际列表或集合中。
答案 2 :(得分:23)
我会像这样实现它:
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;
}
}
答案 3 :(得分:13)
我现在这样做的方式比已经建议的方式要短一些,据我所知,给出了理想的结果:
var index = haystack.ToList().IndexOf(needle);
它有点笨拙,但它完成了工作并且相当简洁。
答案 4 :(得分:6)
我认为最好的选择是这样实现:
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;
}
它也不会创建匿名对象
答案 5 :(得分:5)
游戏有点晚,我知道......但这就是我最近所做的。它与您的略有不同,但允许程序员指定相等操作需要什么(谓词)。我在处理不同类型时发现它非常有用,因为无论对象类型如何,我都有一种通用的方法,而且<T>
内置了相等运算符。
它的内存占用非常小,并且非常非常快速/高效...如果你关心它。
更糟糕的是,您只需将其添加到扩展程序列表中即可。
无论如何......在这里。
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;
}
希望这有助于某人。
答案 6 :(得分:5)
几年后,但是这会使用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);
}
'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);
}
答案 7 :(得分:4)
获得该职位的最佳方法是FindIndex
此功能仅适用于List<>
实施例
int id = listMyObject.FindIndex(x => x.Id == 15);
如果你有枚举器或数组使用这种方式
int id = myEnumerator.ToList().FindIndex(x => x.Id == 15);
或
int id = myArray.ToList().FindIndex(x => x.Id == 15);
答案 8 :(得分:1)
在事实之后找到索引的另一种方法是包装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();
}
}
给出了一个用例:
var items = new[] {1, 2, 3};
var indexedItems = items.ToIndexed();
foreach (var item in indexedItems)
{
Console.WriteLine("items[{0}] = {1}", item.Index, item.Value);
}
答案 9 :(得分:1)
今天在寻找答案时偶然发现了这一点,我想我会把我的版本添加到列表中(没有双关语)。它使用了c#6.0的空条件运算符
IEnumerable<Item> collection = GetTheCollection();
var index = collection
.Select((item,idx) => new { Item = item, Index = idx })
//or .FirstOrDefault(_ => _.Item.Prop == something)
.FirstOrDefault(_ => _.Item == itemToFind)?.Index ?? -1;
我做了一些'老马赛车'(测试)和大型系列赛(~100,000),最糟糕的情况是你想要的项目结束,这是 2x 更快比做ToList().FindIndex()
。如果您想要的项目位于中间, ~4x 更快。
对于较小的集合(~10,000),它似乎只是稍微快一点
以下是我如何测试 https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e
答案 10 :(得分:1)
使用@Marc Gravell的答案,我找到了一种使用以下方法的方法:
source.TakeWhile(x => x != value).Count();
为了在找不到该项目时获得-1:
internal static class Utils
{
public static int IndexOf<T>(this IEnumerable<T> enumerable, T item) => enumerable.IndexOf(item, EqualityComparer<T>.Default);
public static int IndexOf<T>(this IEnumerable<T> enumerable, T item, EqualityComparer<T> comparer)
{
int index = enumerable.TakeWhile(x => comparer.Equals(x, item)).Count();
return index == enumerable.Count() ? -1 : index;
}
}
我想这种方式可能既最快又更简单。但是,我尚未测试性能。
答案 11 :(得分:0)
这可以通过扩展(充当代理)变得非常酷,例如:
collection.SelectWithIndex();
// vs.
collection.Select((item, index) => item);
这将自动将索引分配给可通过此Index
属性访问的集合。
接口:
public interface IIndexable
{
int Index { get; set; }
}
自定义扩展(可能对使用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();