不区分大小写的列表搜索


144

我有一个testList包含一串字符串的列表。testList如果列表中尚不存在新的字符串,我想将其添加到字符串中。因此,我需要对列表进行不区分大小写的搜索并使其高效。我不能使用,Contains因为那没有考虑到外壳。ToUpper/ToLower由于性能原因,我也不想使用。我遇到了这种方法,该方法有效:

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

可以,但是也可以匹配部分单词。如果列表中包含“山羊”,则无法添加“燕麦”,因为它声称“燕麦”已在列表中。有没有一种方法可以以不区分大小写的方式有效地搜索列表,而单词必须完全匹配?谢谢

Answers:


180

可以使用String.Equals而不是String.IndexOf 来确保没有部分匹配。也不要使用FindAll,因为它遍历每个元素,请使用FindIndex(它停在它遇到的第一个元素上)。

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

交替使用一些LINQ方法(它也会在遇到的第一个方法上停止)

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

补充一下,在一些快速测试中,第一种方法似乎快了50%。也许其他人可以确认/否认。
2010年

8
从.NET 2.0开始,现在可以轻松完成此操作-查看下面的shaxby答案。

3
包含方法shaxby的引用(具有IEqualityComparer的重载)是LINQ的一部分,因此自.NET 2.0以来肯定不可用。只是StringComparer类已经存在了一段时间。List <T>没有该方法,ArrayList或StringCollection也没有(他很容易将其称为“列表”的东西)。
亚当·希尔斯

好吧,因为我实际上需要索引,所以这绝对是我的最佳选择。
Nyerguds 2014年

1
第一个解决方案应使用List<>.Exists(Predicate<>)实例方法。另请注意,如果列表中包含null条目,则可能会爆炸。在这种情况下,说起来keyword.Equals(x, StringComparison.OrdinalIgnoreCase)x.Equals(keyword, StringComparison.OrdinalIgnoreCase)(如果您可以保证keyword永远不会为null)更安全。
Jeppe Stig Nielsen

359

我意识到这是一篇老文章,但是以防万一其他人在寻找,您可以Contains通过提供不区分大小写的字符串相等比较器使用它,如下所示:

using System.Linq;

// ...

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

从.net 2.0开始,根据msdn可以使用此功能


21
绝对是这里最好的答案。:)

22
Enumerable <T>。包含(您所引用的内容)自.NET 2.0以来就没有出现。没有List <T>。包含您正在使用的重载。
亚当·希尔斯

@AdamSills对。List <T>中没有此类包含方法。而且,如果它是一个惰性集合,那么它可以像其他Enumerable <T>方法那样对其进行多次迭代。恕我直言,这种方法不应该在这种情况下使用,因为在这种情况下它不太合逻辑。
Sergey Litvinov

40
我起初也没有看到这种过载,但是您需要使用System.Linq进行添加,然后它才会出现。
2014年

1
StringComparer自2.0级一直围绕,但包含的是超载3.5中引入的。 msdn.microsoft.com/zh-CN/library/bb339118(v=vs.110).aspx
Denise Skidmore

18

基于上面的Adam Sills答案-这是一个包含Contains ...的不错的扩展方法::)

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

    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”,否则您将不会看到.Contains的重载。
朱利安·梅尔维尔

1

基于Lance Larsen的答案-这是带有建议字符串的扩展方法。比较而不是字符串。等于

强烈建议您使用带有StringComparison参数的String.Compare重载。这些重载不仅使您可以定义所需的确切比较行为,使用它们还可以使您的代码对其他开发人员更具可读性。[ Josh Free @ 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

您正在检查IndexOf的结果是否大于或等于0,这意味着匹配是否从字符串中的任何位置开始。尝试检查它是否等于 0:

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

现在,“ goat”和“ goat”将不匹配,但是将“ goat”和“ goa”匹配。为避免这种情况,您可以比较两个字符串的长度。

为避免所有这些麻烦,可以使用字典而不是列表。它们的键将是小写字符串,而值将是真实字符串。这样一来,性能就不会受到影响,因为您不必ToLower为每个比较都使用,但是您仍然可以使用Contains


0

以下是在整个列表中搜索关键字并删除该项目的示例:

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

如果要删除在Text属性中包含某些关键字的书,则可以创建关键字列表并将其从书列表中删除:

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

我有一个类似的问题,我需要该项目的索引,但是它必须不区分大小写,我在网上浏览了几分钟,却一无所获,所以我只是写了一个小方法来完成它,这就是我的目的做了:

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

将此代码添加到同一文件,并按以下方式调用它:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

希望这有帮助,祝你好运!


1
为什么要产生第二个清单?那不是很有效。for(var i = 0; i <itemsList.Count; i ++){if(item.ToLower()== searchItem.ToLower()){return i}}
wesm 2014年

我想我们永远不会知道。
丹尼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.