Büyük / Küçük Harfe Duyarsız Liste Arama


144

testListDizeleri bir grup içeren bir liste var. testListListede zaten yoksa sadece yeni bir dize eklemek istiyorum . Bu nedenle, listeyi büyük / küçük harfe duyarlı olmayan bir arama yapmam ve verimli hale getirmem gerekiyor. Kullanamıyorum Containsçünkü bu kasayı dikkate almıyor. ToUpper/ToLowerPerformans nedeniyle de kullanmak istemiyorum . İşe yarayan bu yönteme rastladım:

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

Bu işe yarıyor, ama aynı zamanda kısmi kelimelerle eşleşiyor. Liste "keçi" içeriyorsa "yulaf" ekleyemiyorum çünkü "yulaf" ın zaten listede olduğunu iddia ediyor. Listeleri, kelimelerin tam olarak eşleşmesi gereken büyük / küçük harfe duyarlı olmayan bir şekilde aramak için bir yol var mı? Teşekkürler

Yanıtlar:


181

String.IndexOf yerine, kısmi eşleşmeleriniz olmadığından emin olmak için String.Equals kullanın. Ayrıca FindAll'ı her öğeden geçerken kullanmayın, FindIndex'i kullanın (vurduğu ilk öğede durur).

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

Alternatif olarak bazı LINQ yöntemlerini kullanın (ilk vurduğu yerde de durur)

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

Eklemek gerekirse, birkaç hızlı testte, ilk yöntemin yaklaşık% 50 daha hızlı olduğu görülüyor. Belki başka biri bunu onaylayabilir / reddedebilir.
Brap

8
.NET 2.0'dan itibaren, bu artık kolayca yapılabilir - aşağıdaki shaxby'nin cevabına bakın.
Joe

3
Contains yöntemi shaxby'nin başvurusu (IEqualityComparer'ı alan bir aşırı yüke sahiptir) LINQ'nun bir parçasıdır, bu yüzden kesinlikle .NET 2.0'dan beri mevcut değildir. Sadece StringComparer sınıfı bir süredir var. Listede <T> bu yöntem veya ArrayList veya StringCollection yoktur (kolayca 'liste' olarak atıfta bulunabileceği şeyler).
Adam

Aslında endekse ihtiyacım olduğu için bu kesinlikle benim için en iyi cevaptı.
Nyerguds

1
İlk çözüm List<>.Exists(Predicate<>)örnek yöntemini kullanmalıdır . Ayrıca liste giriş içeriyorsa nullbu durumun patlayabileceğini unutmayın . Durumda, demek daha güvenlidir keyword.Equals(x, StringComparison.OrdinalIgnoreCase)daha x.Equals(keyword, StringComparison.OrdinalIgnoreCase)(bunu garanti edemez eğer keywordboş asla).
Jeppe Stig Nielsen

361

Bu eski bir yazı olduğunun farkındayım ama sadece başka vaka herkes bakıyor içinde, sen yapabilirsiniz kullanmak Containsşöyle harf duyarsız dize eşitlik karşılaştırıcısı sağlayarak:

using System.Linq;

// ...

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

Bu, msdn'ye göre .net 2.0'dan beri mevcuttur .


21
Kesinlikle burada en iyi cevap. :)
Joe

23
Numaralandırılabilir <T> .Contains (ne referansta bulunduğunuz) .NET 2.0'dan beri ortalıkta yok. Liste <T> yok. Kullandığınız aşırı yüke sahip.
Adam

@AdamSills doğru. Liste <T> 'de böyle bir yöntem yoktur. Ve eğer tembel bir koleksiyon ise, diğer Numaralandırılabilir <T> yöntemlerinin yaptığı gibi birkaç kez yineleyebilir. Imho, bu yöntem bu gibi durumlar için kullanılmamalıdır, çünkü o dava için mantıklı değildir.
Sergey Litvinov

41
İlk başta bu aşırı yüklenmeyi görmedim, ancak System.Linq kullanarak eklemeniz gerekiyor.
Michael

2
StringComparerSınıf 2.0 yana yaklaşık edilmiş, ancak İçeriyor o aşırı yük 3.5'de tanıtıldı. msdn.microsoft.com/tr-tr/library/bb339118(v=vs.110).aspx
Denise Skidmore

18

Yukarıdaki Adam Sills cevabına dayanarak - işte İçeriyor için güzel ve temiz bir uzantı yöntemi ... :)

///----------------------------------------------------------------------
/// <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

StringComparer'ı kullanabilirsiniz:

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

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

1
"Using System.Linq" eklediğiniz sürece, aksi takdirde .Contains için aşırı yük göremezsiniz.
Julian Melville

1

Lance Larsen cevabına göre - burada önerilen string ile bir uzatma yöntemi var. String yerine karşılaştırın.

Bir StringComparison parametresi alan aşırı bir String.Compare dosyası kullanmanız önerilir. Bu aşırı yükler, yalnızca tam olarak istediğiniz karşılaştırma davranışını tanımlamanıza izin vermekle kalmaz, bunları kullanmak kodunuzu diğer geliştiriciler için daha okunabilir hale getirir. [ Josh Free @ BCL Takım Blogu ]

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

IndexOf sonucunun daha büyük veya 0'a eşit olup olmadığını kontrol edersiniz, bu da eşleşmenin dizede herhangi bir yerde başlayıp başlamadığını gösterir . 0'a eşit olup olmadığını kontrol etmeyi deneyin :

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

Şimdi "keçi" ve "yulaf" eşleşmeyecek, ancak "keçi" ve "goa" eşleşecek. Bundan kaçınmak için, iki dizenin uzunluklarını karşılaştırabilirsiniz.

Tüm bu komplikasyonları önlemek için liste yerine sözlük kullanabilirsiniz. Onlar anahtar küçük harfli ve değer gerçek dize olurdu. Bu şekilde performans zarar görmez, çünkü ToLowerher karşılaştırma için kullanmak zorunda değilsiniz , ancak yine de kullanabilirsiniz Contains.


0

Aşağıda, tüm listede bir anahtar kelime arama ve bu öğeyi kaldırma örneği verilmiştir:

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

Metin özelliğinde bazı anahtar kelimeler içeren bir kitabı kaldırmak istiyorsanız, bir anahtar kelime listesi oluşturabilir ve kitap listesinden kaldırabilirsiniz:

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

Benzer bir sorunum vardı, öğenin dizinine ihtiyacım vardı ama büyük / küçük harfe duyarsız olmalıydım, birkaç dakika boyunca web'e baktım ve hiçbir şey bulamadım, bu yüzden bunu yapmak için sadece küçük bir yöntem yazdım, işte ne yaptı:

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

Bu kodu aynı dosyaya ekleyin ve şöyle adlandırın:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

Umarım bu yardımcı olur, iyi şanslar!


1
neden ikinci bir liste üretelim? Bu çok verimli değil. for (var i = 0; i <itemsList.Count; i ++) {if (item.ToLower () == searchItem.ToLower ()) {dönüş i}}
wesm 18:14

Sanırım hiçbir zaman bilemeyeceğiz.
Denny
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.