Tìm kiếm danh sách không phân biệt chữ hoa chữ thường


144

Tôi có một danh sách testListchứa một chuỗi các chuỗi. Tôi muốn thêm một chuỗi mới vào testListchỉ khi nó không tồn tại trong danh sách. Do đó, tôi cần thực hiện tìm kiếm không phân biệt chữ hoa chữ thường và làm cho nó hiệu quả. Tôi không thể sử dụng Containsvì điều đó không tính đến vỏ. Tôi cũng không muốn sử dụng ToUpper/ToLowervì lý do hiệu suất. Tôi đã xem qua phương pháp này, hoạt động:

    if(testList.FindAll(x => x.IndexOf(keyword, 
                       StringComparison.OrdinalIgnoreCase) >= 0).Count > 0)
       Console.WriteLine("Found in list");

Điều này hoạt động, nhưng nó cũng phù hợp với một phần từ. Nếu danh sách chứa "dê", tôi không thể thêm "yến mạch" vì nó tuyên bố rằng "yến mạch" đã có trong danh sách. Có cách nào để tìm kiếm danh sách một cách hiệu quả trong trường hợp không nhạy cảm, trong đó các từ phải khớp chính xác? cảm ơn

Câu trả lời:


180

Thay vì String.IndexOf, hãy sử dụng String.Equals để đảm bảo bạn không có kết quả khớp một phần. Ngoài ra, đừng sử dụng FindAll khi đi qua mọi phần tử, hãy sử dụng Find Index (nó dừng ở phần tử đầu tiên mà nó chạm).

if(testList.FindIndex(x => x.Equals(keyword,  
    StringComparison.OrdinalIgnoreCase) ) != -1) 
    Console.WriteLine("Found in list"); 

Luân phiên sử dụng một số phương thức LINQ (cũng dừng trên phương thức đầu tiên mà nó đạt được)

if( testList.Any( s => s.Equals(keyword, StringComparison.OrdinalIgnoreCase) ) )
    Console.WriteLine("found in list");

Chỉ cần thêm, trong một vài thử nghiệm nhanh, dường như phương pháp đầu tiên nhanh hơn khoảng 50%. Có lẽ người khác có thể xác nhận / từ chối điều đó.
Brap

8
Kể từ .NET 2.0, việc này giờ đây đã được thực hiện dễ dàng - hãy xem câu trả lời của shaxby bên dưới.
Joe

3
Phương thức tham chiếu của shaxby (có quá tải cần IEqualityComparer) là một phần của LINQ, do đó chắc chắn nó không có sẵn kể từ .NET 2.0. Chỉ có lớp StringComparer đã xuất hiện được một thời gian. Danh sách <T> không có phương thức đó, cũng không có ArrayList hoặc StringCollection (những thứ mà anh ta có thể dễ dàng được gọi là 'danh sách' của mình).
Adam Sills

Chà, vì tôi thực sự cần chỉ số, đây chắc chắn là câu trả lời tốt nhất cho tôi.
Nyerguds

1
Các giải pháp đầu tiên nên sử dụng List<>.Exists(Predicate<>)phương pháp ví dụ. Cũng lưu ý rằng nếu danh sách chứa nullcác mục, điều này có thể nổ tung. Trong trường hợp đó, nó an toàn hơn để nói keyword.Equals(x, StringComparison.OrdinalIgnoreCase)hơn x.Equals(keyword, StringComparison.OrdinalIgnoreCase)(nếu bạn có thể đảm bảo rằng điều đó keywordkhông bao giờ là null).
Jeppe Stig Nielsen

359

Tôi nhận ra đây là một bài viết cũ, nhưng chỉ trong trường hợp bất kỳ ai khác đang tìm kiếm, bạn có thể sử dụng Containsbằng cách cung cấp trường hợp so sánh chuỗi bằng không nhạy cảm như vậy:

using System.Linq;

// ...

if (testList.Contains(keyword, StringComparer.OrdinalIgnoreCase))
{
    Console.WriteLine("Keyword Exists");
}

Điều này đã có sẵn kể từ .net 2.0 theo msdn .


21
Chắc chắn câu trả lời tốt nhất ở đây. :)
Joe

22
Vô số <T> .Contains (những gì bạn đang tham khảo) đã không xuất hiện kể từ .NET 2.0. Không có Danh sách <T>. Nội dung có quá tải bạn đang sử dụng.
Adam Sills

@AdamSills đúng. Không có phương thức chứa như vậy trong Danh sách <T>. Và nếu đó là một bộ sưu tập lười biếng, thì nó có thể lặp đi lặp lại một vài lần như các phương pháp <T> khác. Imho, phương pháp này không nên được sử dụng cho những trường hợp như vậy, vì nó không hợp lý cho trường hợp đó.
Serge Litvinov

40
Ban đầu tôi cũng không thấy sự quá tải này, nhưng bạn cần thêm bằng System.Linq sau đó nó xuất hiện.
Michael

1
Các StringComparerlớp đã được khoảng từ 2.0, nhưng điều đó quá tải của Có được giới thiệu vào 3.5. msdn.microsoft.com/en-us/l Library / bb339118 (v = vs.110) .aspx
Denise Skidmore

18

Dựa trên câu trả lời của Adam Sills ở trên - đây là một phương pháp tiện ích mở rộng sạch đẹp cho Chứa ... :)

///----------------------------------------------------------------------
/// <summary>
/// Determines whether the specified list contains the matching string value
/// </summary>
/// <param name="list">The list.</param>
/// <param name="value">The value to match.</param>
/// <param name="ignoreCase">if set to <c>true</c> the case is ignored.</param>
/// <returns>
///   <c>true</c> if the specified list contais the matching string; otherwise, <c>false</c>.
/// </returns>
///----------------------------------------------------------------------
public static bool Contains(this List<string> list, string value, bool ignoreCase = false)
{
    return ignoreCase ?
        list.Any(s => s.Equals(value, StringComparison.OrdinalIgnoreCase)) :
        list.Contains(value);
}

10

Bạn có thể sử dụng StringComparer:

    var list = new List<string>();
    list.Add("cat");
    list.Add("dog");
    list.Add("moth");

    if (list.Contains("MOTH", StringComparer.OrdinalIgnoreCase))
    {
        Console.WriteLine("found");
    }

1
Miễn là bạn thêm "bằng System.Linq", nếu không bạn sẽ không thấy sự quá tải đó cho .Contains.
Julian Melville

1

Dựa trên câu trả lời của Lance Larsen - đây là một phương thức mở rộng với chuỗi được đề xuất. Hãy thay vì chuỗi.Equals

Rất khuyến khích bạn sử dụng quá tải String.Compare lấy tham số StringComparison. Những quá tải này không chỉ cho phép bạn xác định hành vi so sánh chính xác mà bạn dự định, sử dụng chúng cũng sẽ giúp mã của bạn dễ đọc hơn đối với các nhà phát triển khác. [ Josh miễn phí @ Blog nhóm BCL ]

public static bool Contains(this List<string> source, string toCheck, StringComparison comp)
{
    return
       source != null &&
       !string.IsNullOrEmpty(toCheck) &&
       source.Any(x => string.Compare(x, toCheck, comp) == 0);
}

0

Bạn đang kiểm tra xem kết quả của IndexOf lớn hơn hoặc bằng 0, nghĩa là trận đấu có bắt đầu ở bất kỳ đâu trong chuỗi không. Hãy thử kiểm tra xem nó có bằng 0 không:

if (testList.FindAll(x => x.IndexOf(keyword, 
                   StringComparison.OrdinalIgnoreCase) >= 0).Count > 0)
   Console.WriteLine("Found in list");

Bây giờ "dê" và "yến" sẽ không khớp, nhưng "dê" và "dê" sẽ. Để tránh điều này, bạn có thể so sánh chiều dài của hai chuỗi.

Để tránh tất cả sự phức tạp này, bạn có thể sử dụng từ điển thay vì danh sách. Khóa của chúng sẽ là chuỗi chữ thường và giá trị sẽ là chuỗi thực. Bằng cách này, hiệu suất không bị tổn thương vì bạn không phải sử dụng ToLowercho mỗi so sánh, nhưng bạn vẫn có thể sử dụng Contains.


0

Dưới đây là ví dụ về tìm kiếm từ khóa trong toàn bộ danh sách và xóa mục đó:

public class Book
{
  public int BookId { get; set; }
  public DateTime CreatedDate { get; set; }
  public string Text { get; set; }
  public string Autor { get; set; }
  public string Source { get; set; }
}

Nếu bạn muốn xóa một cuốn sách có chứa một số từ khóa trong thuộc tính Văn bản, bạn có thể tạo một danh sách các từ khóa và xóa nó khỏi danh sách các cuốn sách:

List<Book> listToSearch = new List<Book>()
   {
        new Book(){
            BookId = 1,
            CreatedDate = new DateTime(2014, 5, 27),
            Text = " test voprivreda...",
            Autor = "abc",
            Source = "SSSS"

        },
        new Book(){
            BookId = 2,
            CreatedDate = new DateTime(2014, 5, 27),
            Text = "here you go...",
            Autor = "bcd",
            Source = "SSSS"


        }
    };

var blackList = new List<string>()
            {
                "test", "b"
            }; 

foreach (var itemtoremove in blackList)
    {
        listToSearch.RemoveAll(p => p.Source.ToLower().Contains(itemtoremove.ToLower()) || p.Source.ToLower().Contains(itemtoremove.ToLower()));
    }


return listToSearch.ToList();

-1

Tôi có một vấn đề tương tự, tôi cần chỉ số của mặt hàng nhưng nó không nhạy cảm, tôi đã tìm kiếm trên mạng trong vài phút và không tìm thấy gì, vì vậy tôi chỉ viết một phương pháp nhỏ để hoàn thành nó, đây là những gì tôi làm đã làm:

private static int getCaseInvariantIndex(List<string> ItemsList, string searchItem)
{
    List<string> lowercaselist = new List<string>();

    foreach (string item in ItemsList)
    {
        lowercaselist.Add(item.ToLower());
    }

    return lowercaselist.IndexOf(searchItem.ToLower());
}

Thêm mã này vào cùng một tệp và gọi nó như thế này:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

Hy vọng điều này sẽ giúp, chúc may mắn!


1
Tại sao sản xuất một danh sách thứ hai? Điều đó không hiệu quả lắm. for (var i = 0; i <itemsList.Count; i ++) {if (item.ToLower () == searchItem.ToLower ()) {return i}}
wesm 18/12/14

Tôi đoán chúng ta sẽ không bao giờ biết.
Denny
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.