Найти элемент в списке по LINQ?


226

Здесь у меня есть простой пример, чтобы найти элемент в списке строк. Обычно я использую для цикла или анонимного делегата, чтобы сделать это так:

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 anonymous delegate
      string foundItem = _list.Find( delegate(string item) {
         found++;
         return string.Equals(search, item);
      });
      */
   }
   return found;
}

LINQ является новым для меня. Мне интересно, могу ли я использовать LINQ, чтобы найти элемент в списке? Как, если возможно?


Замечательно. Тем не менее, все это стиль выражения лямда. Я использую простой список здесь. Список может быть классом с несколькими свойствами, и некоторые из них используются для поиска. Таким образом, любой способ LINQ для поиска, как "из ... в ... где ... выберите ..."
David.Chu.ca

Нет, прости Большинство из этих методов (First, Single, Any, ...) не могут быть напрямую переведены в эту форму.
Р. Мартиньо Фернандес

Неважно, на самом деле вы можете избавиться от лямбд в нескольких случаях ...
Р. Мартиньо Фернандес

Отличные ответы! Я просто хочу получить представление о поиске LINQ из перечисления.
David.Chu.ca

Ответы:


478

Есть несколько способов (обратите внимание, что это не полный список).

1) Single вернет один результат, но сгенерирует исключение, если не найдет ни одного, или более одного (что может или не может быть тем, что вы хотите):

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

Примечание SingleOrDefault()будет вести себя так же, за исключением того, что оно будет возвращать null для ссылочных типов или значение по умолчанию для типов значений вместо исключения.

2) Где будут возвращаться все элементы, которые соответствуют вашим критериям, поэтому вы можете получить IEnumerable с одним элементом:

IEnumerable<string> results = myList.Where(s => s == search);

3) Сначала вернет первый элемент, который соответствует вашим критериям:

string result = myList.First(s => s == search);

Примечание FirstOrDefault()будет вести себя так же, за исключением того, что оно будет возвращать null для ссылочных типов или значение по умолчанию для типов значений вместо исключения.


35
Отличный ответ. Я обнаружил, что SingleOrDefault является моим предпочтительным ответом - так же, как Single, но возвращает null, если не может его найти.
Эдди Паркер

2
Я не знал ни Single (), ни SingleOrDefault (). Очень полезно.
Драконис

Могут ли эти методы использоваться с другими коллекциями, такими как ReadOnlyCollectionили ObservableCollection?
yellavon

@yellavon это методы расширения для любого типа, который реализует IEnumerable<T>илиIQueryable<T>
Rex M

4
При использовании SingleOrDefault следует обратить внимание на то, что, поскольку он выдает исключение, если в списке присутствует более одного совпадения, он должен выполнять итерацию по каждому элементу, где FirstOrDefault прекращает поиск после обнаружения первого совпадения. msdn.microsoft.com/en-us/library/bb342451(v=vs.110).aspx
DavidWainwright

73

Если вы хотите индекс элемента, это сделает это:

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

Вы не можете избавиться от лямбды в первом проходе.

Обратите внимание, что это бросит, если элемент не существует. Это решает проблему, прибегая к обнуляемым типам:

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с индексом, чтобы покрыть случай, когда нужен индекс элемента (как в примере в самом вопросе).
Павел Минаев

Я еще не счастлив. Там нет индекса -1 (не найден) в моем примере. Любое предложение?
Р. Мартиньо Фернандес

Помимо проверки его существования с помощью if, first.
Р. Мартиньо Фернандес

Нужно ли сначала проверять, является ли список пустым?
David.Chu.ca

Выберите броски, ArgumentNullExceptionесли источник нулевой
Р. Мартиньо Фернандес

13

Если 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

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 нулевым в виде из .. в _list ...?
David.Chu.ca

6

Этот метод проще и безопаснее

var lOrders = new List<string>();

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


1
Я думаю, что нам даже не нужно 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

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

Вот один из способов переписать ваш метод для использования 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


1

Попробуйте этот код:

return context.EntitytableName.AsEnumerable().Find(p => p.LoginID.Equals(loginID) && p.Password.Equals(password)).Select(p => new ModelTableName{ FirstName = p.FirstName, UserID = p.UserID });

1

Если нам нужно найти элемент из списка, то мы можем использовать метод Findи FindAllрасширения, но между ними есть небольшая разница. Вот пример.

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

1

Это поможет вам получить первое значение или значение по умолчанию в поиске Linq List

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

Этот поиск найдет первое или заданное по умолчанию значение.


0

Вы хотите найти объект в списке объектов.

Это поможет вам получить первое значение или значение по умолчанию в поиске Linq List.

var item = list.FirstOrDefault(items =>  items.Reference == ent.BackToBackExternalReferenceId);

или

var item = (from items in list
    where items.Reference == ent.BackToBackExternalReferenceId
    select items).FirstOrDefault();

0

Вы можете использовать FirstOfDefault с расширением Where Linq, чтобы получить класс MessageAction из IEnumerable. Reme

var action = Message.Actions.Where (e => e.targetByName == className) .FirstOrDefault ();

где

Список действий {get; устанавливать; }

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.