Pencarian Daftar Tidak Peka Huruf Besar-Kecil

144

Saya memiliki daftar testListyang berisi banyak string. Saya ingin menambahkan string baru ke dalam testListhanya jika belum ada dalam daftar. Oleh karena itu, saya perlu melakukan pencarian daftar case-insensitive dan membuatnya efisien. Saya tidak dapat menggunakan Containskarena itu tidak memperhitungkan casing. Saya juga tidak ingin menggunakan ToUpper/ToLoweruntuk alasan kinerja. Saya menemukan metode ini, yang berfungsi:

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

Ini berfungsi, tetapi juga cocok dengan sebagian kata. Jika daftar berisi "kambing", saya tidak dapat menambahkan "oat" karena mengklaim bahwa "oat" sudah ada dalam daftar. Apakah ada cara untuk secara efisien mencari daftar dalam kasus yang sensitif, di mana kata-kata harus sama persis? Terima kasih

Brap
sumber

Jawaban:

180

Alih-alih String.IndexOf, gunakan String.Equals untuk memastikan Anda tidak memiliki kecocokan sebagian. Juga jangan gunakan FindAll karena itu melewati setiap elemen, gunakan FindIndex (itu berhenti pada yang pertama hits).

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

Bergantian gunakan beberapa metode LINQ (yang juga berhenti pada metode pertama yang dinyalakan)

if( testList.Any( s => s.Equals(keyword, StringComparison.OrdinalIgnoreCase) ) )
    Console.WriteLine("found in list");
Adam Sills
sumber
Sebagai tambahan, dalam beberapa tes cepat, tampaknya metode pertama sekitar 50% lebih cepat. Mungkin orang lain bisa mengkonfirmasi / menyangkal hal itu.
Brap
8
Pada. NET 2.0, ini sekarang mudah dilakukan - lihat jawaban shaxby di bawah ini.
Joe
3
Referensi yang berisi metode shaxby (yang memiliki kelebihan yang mengambil IEqualityComparer) adalah bagian dari LINQ, jadi sudah pasti belum tersedia sejak .NET 2.0. Hanya kelas StringComparer telah ada untuk sementara waktu. Daftar <T> tidak memiliki metode itu, begitu pula ArrayList atau StringCollection (hal-hal yang dengan mudah dapat dirujuk sebagai 'daftar' -nya).
Adam Sills
Yah, karena aku benar - benar membutuhkan indeks, ini pasti jawaban terbaik untukku.
Nyerguds
1
Solusi pertama harus menggunakan List<>.Exists(Predicate<>)metode instance. Perhatikan juga bahwa jika daftar berisi nullentri, ini dapat meledak. Dalam hal ini lebih aman untuk mengatakan keyword.Equals(x, StringComparison.OrdinalIgnoreCase)daripada x.Equals(keyword, StringComparison.OrdinalIgnoreCase)(jika Anda dapat menjamin bahwa keyworditu tidak pernah nol).
Jeppe Stig Nielsen
360

Saya menyadari ini adalah posting lama, tetapi untuk berjaga-jaga jika ada orang lain yang melihat, Anda dapat menggunakan Containsdengan menyediakan pembanding kesetaraan string yang tidak sensitif seperti:

using System.Linq;

// ...

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

Ini telah tersedia sejak .net 2.0 menurut msdn .

heboh
sumber
21
Pasti jawaban terbaik di sini. :)
Joe
23
Enumerable <T> .Contains (apa yang Anda referensikan) belum ada sejak .NET 2.0. Tidak ada Daftar <T> .Berisi yang memiliki kelebihan yang Anda gunakan.
Adam Sills
@AdamSills benar. Tidak ada metode seperti itu dalam Daftar <T>. Dan jika itu koleksi yang malas, maka ia dapat menggunakannya beberapa kali seperti metode <T> Enumerable lainnya. Imho, metode ini tidak boleh digunakan untuk kasus-kasus seperti itu, karena itu tidak begitu logis untuk kasus itu.
Sergey Litvinov
40
Saya tidak melihat kelebihan ini pada awalnya juga, tetapi Anda perlu menambahkan menggunakan System.Linq maka muncul.
Michael
1
The StringComparerkelas telah ada sejak 2.0, tapi itu kelebihan Mengandung diperkenalkan di 3,5. msdn.microsoft.com/en-us/library/bb339118(v=vs.110).aspx
Denise Skidmore
18

Berdasarkan jawaban Adam Sills di atas - inilah metode ekstensi bersih yang bagus untuk Berisi ... :)

///----------------------------------------------------------------------
/// <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);
}
Lance Larsen - Microsoft MVP
sumber
10

Anda dapat menggunakan StringComparer:

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

    if (list.Contains("MOTH", StringComparer.OrdinalIgnoreCase))
    {
        Console.WriteLine("found");
    }
jlo-gmail
sumber
1
Selama Anda menambahkan "using System.Linq", jika tidak, Anda tidak akan melihat kelebihan itu untuk .Berisi.
Julian Melville
1

Berdasarkan jawaban Lance Larsen - inilah metode ekstensi dengan string yang disarankan. Bandingkan, bukan string. Setara

Sangat disarankan agar Anda menggunakan String.Compare yang berlebihan yang menggunakan parameter StringComparison. Kelebihan ini tidak hanya memungkinkan Anda untuk menentukan perilaku perbandingan yang tepat yang Anda maksudkan, menggunakannya juga akan membuat kode Anda lebih mudah dibaca untuk pengembang lain. [ Josh Gratis @ Blog Tim 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);
}
bukan nama
sumber
0

Anda memeriksa apakah hasil dari IndexOf lebih besar atau sama dengan 0, yang berarti apakah pertandingan dimulai di string mana saja . Coba periksa apakah sama dengan 0:

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

Sekarang "kambing" dan "gandum" tidak akan cocok, tetapi "kambing" dan "kambing" akan. Untuk menghindari ini, Anda dapat membandingkan panjang kedua string.

Untuk menghindari semua kerumitan ini, Anda dapat menggunakan kamus alih-alih daftar. Kunci mereka adalah string huruf kecil, dan nilainya akan menjadi string nyata. Dengan cara ini, kinerja tidak sakit karena Anda tidak harus menggunakan ToLoweruntuk setiap perbandingan, tetapi Anda masih bisa menggunakannya Contains.

Ilya Kogan
sumber
0

Di bawah ini adalah contoh mencari kata kunci di seluruh daftar dan menghapus item itu:

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

Jika Anda ingin menghapus buku yang berisi beberapa kata kunci di properti Teks, Anda dapat membuat daftar kata kunci dan menghapusnya dari daftar buku:

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();
Himanshu Chopra
sumber
-1

Saya memiliki masalah yang sama, saya perlu indeks item tetapi itu harus case sensitive, saya melihat-lihat web selama beberapa menit dan tidak menemukan apa-apa, jadi saya hanya menulis metode kecil untuk menyelesaikannya, di sini adalah apa yang saya melakukan:

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

Tambahkan kode ini ke file yang sama, dan beri nama seperti ini:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

Semoga ini bisa membantu, semoga berhasil!

Monyet di Piyama
sumber
1
mengapa menghasilkan daftar kedua? Itu tidak terlalu efisien. for (var i = 0; i <itemsList.Count; i ++) {if (item.ToLower () == searchItem.ToLower ()) {return i}}
wesm
Saya kira kita tidak akan pernah tahu.
Denny