Tìm một mục trong Danh sách của LINQ?


226

Ở đây tôi có một ví dụ đơn giản để tìm một mục trong danh sách các chuỗi. Thông thường tôi sử dụng cho vòng lặp hoặc đại biểu ẩn danh để làm điều đó như thế này:

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 là mới đối với tôi. Tôi tò mò liệu tôi có thể sử dụng LINQ để tìm mục trong danh sách không? Làm thế nào nếu có thể?


Thật tuyệt. Tuy nhiên, đó là tất cả các phong cách biểu hiện lamda. Tôi sử dụng một danh sách đơn giản ở đây. Danh sách có thể là một lớp với một số thuộc tính và một số được sử dụng để tìm kiếm. Vì vậy, bất kỳ cách LINQ nào để tìm kiếm như "từ .. trong ... nơi ... chọn ..."
David.Chu.ca

Không, xin lỗi. Hầu hết các phương thức này (Đầu tiên, Đơn, Bất kỳ, ...) không thể được dịch trực tiếp sang biểu mẫu đó.
R. Martinho Fernandes

Không bao giờ, thực sự bạn có thể thoát khỏi lambdas trong một vài trường hợp ...
R. Martinho Fernandes

Câu trả lời tuyệt vời! Tôi chỉ muốn có được một hương vị của LINQ tìm kiếm từ trường hợp liệt kê.
David.Chu.ca

Câu trả lời:


478

Có một vài cách (lưu ý đây không phải là một danh sách đầy đủ).

1) Độc thân sẽ trả về một kết quả duy nhất, nhưng sẽ đưa ra một ngoại lệ nếu nó không tìm thấy hoặc nhiều hơn một (có thể hoặc không thể là điều bạn muốn):

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

Lưu ý SingleOrDefault()sẽ hành xử giống nhau, ngoại trừ nó sẽ trả về null cho các loại tham chiếu hoặc giá trị mặc định cho các loại giá trị, thay vì ném một ngoại lệ.

2) Trường hợp sẽ trả về tất cả các mục phù hợp với tiêu chí của bạn, do đó bạn có thể nhận được một IEnumerable với một yếu tố:

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

3) Đầu tiên sẽ trả về mục đầu tiên phù hợp với tiêu chí của bạn:

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

Lưu ý FirstOrDefault()sẽ hành xử giống nhau, ngoại trừ nó sẽ trả về null cho các loại tham chiếu hoặc giá trị mặc định cho các loại giá trị, thay vì ném một ngoại lệ.


35
Câu trả lời chính xác. Tôi thấy SingleOrDefault là câu trả lời lựa chọn của tôi - giống như Single nhưng trả về 'null' nếu không thể tìm thấy nó.
Eddie Parker

2
Tôi không biết Single () hoặc SingleOrDefault (). Rất hữu ích.
draconis

Những phương pháp này có thể được sử dụng với các bộ sưu tập khác quá ReadOnlyCollectionhay ObservableCollectionkhông?
yellavon

@yellavon đây là các phương thức mở rộng trên bất kỳ loại nào thực hiện IEnumerable<T>hoặcIQueryable<T>
Rex M

4
Một điều cần lưu ý khi sử dụng SingleOrDefault là bởi vì nó ném một ngoại lệ nếu có nhiều hơn một trận đấu trong danh sách, nó phải lặp lại qua mọi mục, trong đó FirstOrDefault sẽ ngừng tìm kiếm khi tìm thấy kết quả khớp đầu tiên. msdn.microsoft.com/en-us/l
Library / bb342451 (v = vs.110) .aspx

73

Nếu bạn muốn chỉ mục của phần tử, điều này sẽ làm điều đó:

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

Bạn không thể thoát khỏi lambda trong lần đầu tiên.

Lưu ý rằng điều này sẽ ném nếu vật phẩm không tồn tại. Điều này giải quyết vấn đề bằng cách dùng đến ints nullable:

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

Nếu bạn muốn các mục:

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

Nếu bạn muốn đếm số lượng mặt hàng phù hợp:

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

Nếu bạn muốn tất cả các mục phù hợp:

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

Và đừng quên kiểm tra danh sách nulltrong bất kỳ trường hợp nào.

Hoặc dùng (list ?? Enumerable.Empty<string>()) thay vì list.

Cảm ơn Pavel đã giúp đỡ trong các ý kiến.


2
Hai điểm. Đầu tiên, không có nhu cầu thực sự sử dụng string.Equalsở đây - không có gì sai với ==. Thứ hai, tôi cũng đề cập đến FirstOrDefault(đối với trường hợp vật phẩm có thể không có ở đó) và Selectvới chỉ mục để bao quát trường hợp cần chỉ mục của vật phẩm (vì nó nằm trong mẫu trong chính câu hỏi).
Pavel Minaev

Tôi chưa hạnh phúc Không có chỉ số -1 (không tìm thấy) trong ví dụ của tôi. Bất kì lời đề nghị nào?
R. Martinho Fernandes

Bên cạnh việc kiểm tra sự tồn tại của nó với if, đầu tiên.
R. Martinho Fernandes

Tôi có cần kiểm tra xem danh sách có null không trước?
David.Chu.ca

Chọn ném ArgumentNullExceptionnếu nguồn là null
R. Martinho Fernandes

13

Nếu đó thực sự là một List<string>bạn không cần LINQ, chỉ cần sử dụng:

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

Nếu bạn đang tìm kiếm chính nó, hãy thử:

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

1
Theo logic của ví dụ đầu tiên, chúng ta có thể sử dụng _list.Find(search)cho ví dụ thứ hai.
jwg

12

Bạn có muốn các mục trong danh sách hoặc các mục thực tế (sẽ giả sử các mục đó).

Dưới đây là một loạt các tùy chọn cho bạn:

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

Whoa ... một số người siêu nhanh là người kích hoạt;)
Kelsey

Làm thế nào về chỉ số như là một giá trị trở lại?
David.Chu.ca

và tôi có cần kiểm tra xem _list có null ở dạng từ .. trong _list không ...?
David.Chu.ca

6

Phương pháp này dễ dàng và an toàn hơn

var lOrders = new List<string>();

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


1
Tôi nghĩ rằng chúng ta thậm chí không cần true : falsedưới đây nên hoạt động như nhau bool insertOrderNew = lOrders.Find(r => r == "1234") == null;
Vbp

5

Thế còn IndexOf?

Tìm kiếm đối tượng đã chỉ định và trả về chỉ mục của lần xuất hiện đầu tiên trong danh sách

Ví dụ

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

Lưu ý rằng nó trả về -1 nếu giá trị không xảy ra trong danh sách

> boys.IndexOf("Hermione")  
-1

2

Tôi đã từng sử dụng một Từ điển là một loại danh sách được lập chỉ mục sẽ cung cấp cho tôi chính xác những gì tôi muốn khi tôi muốn.

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

Ví dụ, bất cứ khi nào tôi muốn truy cập các giá trị lề của mình, tôi đều giải quyết từ điển của mình:

int xStartPos = margins["left"];
int xLimitPos = margins["right"];
int yStartPos = margins["top"];
int yLimitPos = margins["bottom"];

Vì vậy, tùy thuộc vào những gì bạn đang làm, một từ điển có thể hữu ích.


Câu trả lời tuyệt vời cho một câu hỏi khác nhau.
jwg

2

Đây là một cách để viết lại phương thức của bạn để sử dụng 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;
}

Vì vậy, gọi nó với

GetItemIndex("two")sẽ trở lại 1,

GetItemIndex("notthere") sẽ trở lại -1 .

Tham khảo: linqsamples.com


1

Hãy thử mã này:

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

Nếu chúng ta cần tìm một phần tử từ danh sách, thì chúng ta có thể sử dụng phương thức FindFindAllphần mở rộng, nhưng có một chút khác biệt giữa chúng. Đây là một ví dụ.

 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

Điều này sẽ giúp bạn có được giá trị đầu tiên hoặc mặc định trong tìm kiếm Danh sách Linq của bạn

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

Tìm kiếm này sẽ tìm thấy giá trị đầu tiên hoặc mặc định nó sẽ trả về.


0

Bạn muốn tìm kiếm một đối tượng trong danh sách đối tượng.

Điều này sẽ giúp bạn có được giá trị đầu tiên hoặc mặc định trong tìm kiếm Danh sách Linq của bạn.

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

hoặc là

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

0

Bạn có thể sử dụng FirstOfDefault với tiện ích mở rộng Where Linq để nhận lớp MessageAction từ IEnumerable. Còn lại

var action = Message.Actions.Where (e => e.targetByName == className) .Đầu tiên ();

Ở đâu

Liệt kê các hành động {get; bộ; }

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.