Знайти предмет у Списку за 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

Ні, вибач. Більшість із цих методів (Перший, Одиничний, Будь-який, ...) не можуть бути безпосередньо переведені у цю форму.
Р. Мартіньо Фернандес

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

Чудові відповіді! Мені просто хочеться покуштувати LINQ, який шукає з перерахунку.
David.Chu.ca

Відповіді:


478

Є кілька способів (зауважте, що це не повний перелік).

1) Одномісний поверне один результат, але викине виняток, якщо він не знайде жодного або більше одного (що може бути, а може і не бути тим, що ви хочете):

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

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

2) Де повертаються всі елементи, які відповідають вашим критеріям, тому ви можете отримати IEnumerable з одним елементом:

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

3) Спочатку поверне перший елемент, який відповідає вашим критеріям:

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

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


35
Чудова відповідь. Я знайшов SingleOrDefault як свою відповідь на вибір - такий же, як Single, але повертає "null", якщо він не може його знайти.
Едді Паркер

2
Я не знав ні Single (), ні SingleOrDefault (). Дуже корисний.
draconis

Можуть чи ці методи можна використовувати з іншими колекціями теж , як 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();

Ви не можете позбутися лямбда в перший прохід.

Зауважте, що це буде кинутим, якщо елемент не існує. Це вирішує проблему, вдаючись до змінних ints:

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з індексом, щоб охопити той випадок, коли потрібен індекс елемента (як це є у вибірці у самому запитанні).
Павло Мінаєв

Я ще не задоволений. У моєму прикладі немає жодного індексу (не знайдено). Будь-яка пропозиція?
Р. Мартіньо Фернандес

Крім того, щоб перевірити його наявність, якщо спочатку.
Р. Мартіньо Фернандес

Чи потрібно мені спочатку перевірити, чи список недійсний?
David.Chu.ca

Виберіть кидки, ArgumentNullExceptionякщо джерело є нульовим
R. Martinho Fernandes

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

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

Цей пошук знайде перше або значення за замовчуванням, яке воно поверне.


0

Ви хочете шукати об’єкт у списку об’єктів.

Це допоможе вам отримати перше або значення за замовчуванням у вашому пошуку за списком Linq.

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

де

Список дій {get; набір; }

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.