我该如何在单个步骤中获取列表中项目的索引?

297

如何在不循环遍历列表的情况下找到项目的索引?

目前这看起来不太好 - 为了获取索引而在列表中搜索相同的项目两次:

var oProp = something;

int theThingIActuallyAmInterestedIn = myList.IndexOf(myList.Single(i => i.Prop == oProp));

可能是重复问题:*通过LINQ在列表中查找项* - Peter Mortensen
9个回答

585

那么来看一下List.FindIndex 方法如何使用:

int index = myList.FindIndex(a => a.Prop == oProp);

该方法执行线性搜索;因此,此方法是一个O(n)操作,其中n为Count。

如果未找到该项,则返回-1。


2
int index 怎么样? - Dylan Czenski
2
@DylanChensky 他编写JS的时间太长了。 - lenny
13
如果找不到该项,将返回-1。 - Daniel Filipe
5
@lennyy,如果你不知道的话:在C#中,“var”是完全可以使用的... - ims1234
2
@ims1234 我更喜欢显式类型以提高可读性。除非在实例化时,在一行中不需要两次使用类名。 - lenny

138

对于简单类型,你可以使用"IndexOf":

List<string> arr = new List<string>();
arr.Add("aaa");
arr.Add("bbb");
arr.Add("ccc");
int i = arr.IndexOf("bbb"); // Returns 1.

你如何定义“简单类型”?标量类型? - Peter Mortensen
@PeterMortensen,是的!☺ - Jose Manuel Abarca Rodríguez
1
如果元素未找到,这也将返回-1。正如Jose已经提到的,如果使用List <string>,那么这是正确的方法,但是对于复杂对象的列表来说,这并不可行。 - Ruslan

94

编辑:如果你仅使用List<>,并且只需要索引,则List.FindIndex确实是最佳方法。我将保留此答案供那些需要其他内容(例如在任何IEnumerable<>之上)的人使用。

使用带有索引的谓词的Select重载,这样你就可以将列表转换为(索引,值)对:

var pair = myList.Select((Value, Index) => new { Value, Index })
                 .Single(p => p.Value.Prop == oProp);

然后:

Console.WriteLine("Index:{0}; Value: {1}", pair.Index, pair.Value);

或者,如果你只需要索引,并且在多个地方使用它,你可以轻松编写自己的扩展方法,就像Where一样,但是它返回与谓词匹配的项的索引,而不是原始项。


2
似乎他只需要索引。List<>.FindIndex(Predicate<>)是最好的方法。尽管问题标题可能会暗示其他方面,但OP的描述非常清楚,他只需要“实际感兴趣的theThingIActuallyAmInterestedIn”索引。 - Louis Ricci
1
@LastCoder:啊哈 - 我错过了FindIndex。是的,我完全同意。 - Jon Skeet
1
只是为了明确,"索引/值->单个"方法是否比手动迭代两次更好(在这里意味着在Big-O方面更快)?还是LINQ2Objects提供程序足够聪明,可以优化掉其中一个迭代?(我假设选择和单个通常都是O(n)操作) - sara
1
@kai:我认为你需要仔细了解一下LINQ的工作原理。详细的解释在评论中不太容易,因为它太复杂了。然而……这个操作只会对源集合进行一次迭代。LINQ设置了一个管道,将输入序列惰性地转换成另一个序列,然后Single()操作遍历该序列并找到与谓词匹配的单个项。更多详情请阅读我的edulinq博客系列:http://codeblog.jonskeet.uk/category/edulinq/ - Jon Skeet
1
+1 我需要这个解决方案。老板觉得我聪明了一次。有人建议我仔细记录下来,因为它使用了匿名类型,可能不清楚给领域中的下一个编码者。 - Adam Wells

17
如果您不想使用LINQ,则:

如果您不想使用LINQ,则:

int index;
for (int i = 0; i < myList.Count; i++)
{
    if (myList[i].Prop == oProp)
    {
       index = i;
       break;
    }
}

这种方式只会对列表进行一次迭代。


22
@KingKing 没有人说它是这样的。 - Tomer W
1
这个实现和 Linq 的 FindIndex 是一样的吗? - Paul C
2
可能不是完全相同的代码,List 在某些地方有一些不错的优化。但我很难相信他们可以在小于 O(n) 的时间内搜索无序列表,所以我认为它们在实践中可能非常相似。 - sara

14
  1. 在列表中查找任意字符串值的索引的简单解决方案。

    这里是一个字符串列表的代码:

 int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
  • 查找列表中任何整数值的索引的简单解决方案。

    这是一个整数列表的代码:

  •  int indexOfNumber = myList.IndexOf(/* insert number from list */);
    

    5

    如果有人想了解 Array 版本,可以这样做:

    int i = Array.FindIndex(yourArray, x => x == itemYouWant);
    

    3
    这是一个可复制/粘贴的IEnumerable扩展方法:
    public static class EnumerableExtensions
    {
        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate,
        /// and returns the zero-based index of the first occurrence within the entire <see cref="IEnumerable{T}"/>.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list">The list.</param>
        /// <param name="predicate">The predicate.</param>
        /// <returns>
        /// The zero-based index of the first occurrence of an element that matches the conditions defined by <paramref name="predicate"/>, if found; otherwise it'll throw.
        /// </returns>
        public static int FindIndex<T>(this IEnumerable<T> list, Func<T, bool> predicate)
        {
            var idx = list.Select((value, index) => new {value, index}).Where(x => predicate(x.value)).Select(x => x.index).First();
            return idx;
        }
    }
    

    享受。


    0

    这都很好 - 但是如果您想将现有元素选择为默认值怎么办?在我的问题中,没有“--选择一个值--”选项。

    这是我的代码 - 如果您不想检查没有结果,您可以将其变成一行...

    private void LoadCombo(ComboBox cb, string itemType, string defVal = "")
    {
        cb.DisplayMember = "Name";
        cb.ValueMember = "ItemCode";
        cb.DataSource = db.Items.Where(q => q.ItemTypeId == itemType).ToList();
    
        if (!string.IsNullOrEmpty(defVal))
        {
            var i = ((List<GCC_Pricing.Models.Item>)cb.DataSource).FindIndex(q => q.ItemCode == defVal);
            if (i>=0) cb.SelectedIndex = i;
        }
    }
    

    0
    IndexOf可以直接与对象一起使用,无需显式比较。
    List<YourObject> listObj = new List<YourObject>();
        var obj = new YourObject();
        int idx = listObj.IndexOf(obj);
    

    感谢您对Stack Overflow社区做出贡献的兴趣。这个问题已经有很多答案了,其中一个答案已经得到社区广泛验证。您确定您的方法之前没有被提到过吗?如果是这样的话,能否解释一下您的方法与众不同的地方,在什么情况下您的方法可能更好,并且为什么您认为之前的答案不够满意。您可以编辑您的回答并提供解释吗? - Jeremy Caney

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接