使用LINQ在列表中查找项目

255

这里我有一个简单的例子,用于在字符串列表中查找项目。通常我使用for循环或匿名委托来完成此操作,就像这样:

int GetItemIndex(string search)
{
   int found = -1;
   if ( _list != null )
   {
     foreach (string item in _list) // _list is an instance of List<string>
     {
        found++;
        if ( string.Equals(search, item) )
        {
           break;
        }
      }
      /* Use an anonymous delegate
      string foundItem = _list.Find( delegate(string item) {
         found++;
         return string.Equals(search, item);
      });
      */
   }
   return found;
}

LINQ对我来说是新的。 我能否使用LINQ在列表中查找项目? 如果可能,该如何实现?


太好了。然而,这些都是lambda表达式风格。我在这里使用一个简单的列表。该列表可能是一个带有几个属性的类,并且其中一些用于搜索。因此,是否有任何LINQ搜索方式,例如“from..in...where...select…”? - David.Chu.ca
抱歉,大多数这些方法(First、Single、Any等)不能直接转换成那种形式。 - R. Martinho Fernandes
没关系,实际上在某些情况下你可以摆脱Lambda表达式... - R. Martinho Fernandes
很棒的答案!我只是想尝试一下从枚举情况中进行LINQ搜索。 - David.Chu.ca
14个回答

545

有几种方法(注意这不是一个完整的列表)。

  1. Single将返回单个结果,但如果找到零个或多个结果,就会抛出异常(这可能是你想要的,也可能不是):

 string search = "lookforme";
 List<string> myList = new List<string>();
 string result = myList.Single(s => s == search);

请注意,SingleOrDefault() 的行为与之相同,不同的是对于引用类型它将返回null,对于值类型它将返回默认值,而不是抛出异常。

  1. Where 将返回所有符合您条件的项,因此您可能会得到一个只有一个元素的IEnumerable<string>:

 IEnumerable<string> results = myList.Where(s => s == search);
  • First将返回与您的条件相匹配的第一个项目:

  •  string result = myList.First(s => s == search);
    
    请注意,FirstOrDefault() 的行为与之相同,唯一的区别是对于引用类型它会返回null,而对于值类型它会返回默认值,而不是抛出异常。


    42
    好的回答。我发现SingleOrDefault是我首选的答案 - 与Single相同,但如果找不到则返回“null”。 - Eddie Parker
    3
    我之前不了解Single()和SingleOrDefault()。非常有用。 - draconis
    这些方法也可以用于其他集合,比如ReadOnlyCollectionObservableCollection吗? - yellavon
    @yellavon 这些是在任何实现 IEnumerable<T>IQueryable<T> 接口的类型上的扩展方法。 - Rex M
    这是一个老问题,但在谷歌搜索中仍然很受欢迎,您能否添加FirstOrDefault()? - Martin Verjans
    8
    使用SingleOrDefault需要注意的一点是,如果列表中有多个匹配项,则它会抛出一个异常,因此必须遍历每一项,而FirstOrDefault在找到第一个匹配项后就会停止搜索。 - DavidWainwright

    80

    如果您想获取元素的索引,可以使用以下代码:

    int index = list.Select((item, i) => new { Item = item, Index = i })
                    .First(x => x.Item == search).Index;
    
    // or
    var tagged = list.Select((item, i) => new { Item = item, Index = i });
    int index = (from pair in tagged
                where pair.Item == search
                select pair.Index).First();
    

    在第一遍中,你无法摆脱 lambda。

    请注意,如果该项不存在,这将抛出异常。通过使用可空整数解决该问题:

    var tagged = list.Select((item, i) => new { Item = item, Index = (int?)i });
    int? index = (from pair in tagged
                where pair.Item == search
                select pair.Index).FirstOrDefault();
    
    如果您想获取该项:
    // Throws if not found
    var item = list.First(item => item == search);
    // or
    var item = (from item in list
                where item == search
                select item).First();
    
    // Null if not found
    var item = list.FirstOrDefault(item => item == search);
    // or
    var item = (from item in list
                where item == search
                select item).FirstOrDefault();
    

    如果您想计数与某个条件匹配的项目数量:

    int count = list.Count(item => item == search);
    // or
    int count = (from item in list
                where item == search
                select item).Count();
    

    如果您想获取所有符合条件的项:

    var items = list.Where(item => item == search);
    // or
    var items = from item in list
                where item == search
                select item;
    

    在这些情况下,请不要忘记检查列表中是否存在null

    或者使用(list ?? Enumerable.Empty<string>())代替list


    2
    两点。首先,在这里使用string.Equals没有必要 - 使用==也没有问题。其次,我还会提到FirstOrDefault(对于可能不存在项目的情况),以及使用索引的Select来覆盖需要项索引的情况(就像在问题本身的示例中一样)。 - Pavel Minaev
    我还不满意。我的示例中没有-1索引(未找到)。有什么建议吗? - R. Martinho Fernandes
    我需要先检查列表是否为空吗? - David.Chu.ca
    如果您使用“First”的非抛出版本,则应该可以正常工作。 - R. Martinho Fernandes
    @David:不要指望这样做。那可能会破坏现有的代码,因为异常已经被记录在案了,而且可能有代码期望它出现。 - R. Martinho Fernandes
    显示剩余8条评论

    12

    你是想要在列表中的项还是实际的项本身(我会认为是项本身)。

    以下是一些供您选择的选项:

    string result = _list.First(s => s == search);
    
    string result = (from s in _list
                     where s == search
                     select s).Single();
    
    string result = _list.Find(search);
    
    int result = _list.IndexOf(search);
    

    作为返回值,索引怎么样? - David.Chu.ca
    我需要在从...中的_list中检查是否为空吗? - David.Chu.ca

    12

    如果它确实是一个List<string>,你不需要使用LINQ,只需使用:

    int GetItemIndex(string search)
    {
        return _list == null ? -1 : _list.IndexOf(search);
    }
    

    如果你正在寻找该物品本身,请尝试:

    string GetItem(string search)
    {
        return _list == null ? null : _list.FirstOrDefault(s => s.Equals(search));
    }
    

    1
    按照第一个示例的逻辑,我们可以在第二个示例中使用 _list.Find(search) - jwg

    7
    这种方法更简单、更安全。
    var lOrders = new List<string>();
    

    bool insertOrderNew = lOrders.Find(r => r == "1234") == null ? true : false

    这段代码逻辑是在检查是否存在订单号为"1234"的订单,如果不存在则将insertOrderNew赋值为true,否则赋值为false。

    2
    我认为我们甚至不需要下面的 true : false,应该能够起同样的作用bool insertOrderNew = lOrders.Find(r => r == "1234") == null; - Vbp

    5

    那么关于IndexOf呢?

    在列表中搜索指定的对象,并返回第一个匹配项的索引

    例如:

    > var boys = new List<string>{"Harry", "Ron", "Neville"};  
    > boys.IndexOf("Neville")  
    2
    > boys[2] == "Neville"
    True
    

    请注意,如果该值未在列表中出现,则返回-1。
    > boys.IndexOf("Hermione")  
    -1
    

    4

    这将帮助您获取LINQ列表搜索中的第一个或默认值

    var results = _List.Where(item => item == search).FirstOrDefault();
    

    此搜索将查找第一个或默认值,并返回该值。


    2

    以下是一种使用LINQ重写方法的方式:

    public static int GetItemIndex(string search)
    {
        List<string> _list = new List<string>() { "one", "two", "three" };
    
        var result = _list.Select((Value, Index) => new { Value, Index })
                .SingleOrDefault(l => l.Value == search);
    
        return result == null ? -1 : result.Index;
    }
    

    因此,使用GetItemIndex("two")进行调用将返回1,而使用GetItemIndex("notthere")进行调用将返回-1
    参考:linqsamples.com

    2

    我曾经使用过一本词典,它是一种索引列表,可以在我需要的时候给我想要的准确解释。

    Dictionary<string, int> margins = new Dictionary<string, int>();
    margins.Add("left", 10);
    margins.Add("right", 10);
    margins.Add("top", 20);
    margins.Add("bottom", 30);
    

    每当我想要访问我的边距值时,例如,我会调用我的字典:
    int xStartPos = margins["left"];
    int xLimitPos = margins["right"];
    int yStartPos = margins["top"];
    int yLimitPos = margins["bottom"];
    

    因此,根据您所需的内容,字典可能非常有用。

    不错的回答,虽然与问题不同。 - jwg

    2

    如果我们需要从列表中查找元素,那么可以使用FindFindAll扩展方法,但它们之间有一些微小的差异。这里是一个例子。

     List<int> items = new List<int>() { 10, 9, 8, 4, 8, 7, 8 };
    
      // It will return only one 8 as Find returns only the first occurrence of matched elements.
         var result = items.Find(ls => ls == 8);      
     // this will returns three {8,8,8} as FindAll returns all the matched elements.
          var result1 = items.FindAll(ls => ls == 8); 
    

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