การค้นหารายการแบบคำนึงถึงขนาดตัวพิมพ์


144

ฉันมีรายการtestListที่ประกอบด้วยสตริงจำนวนหนึ่ง ฉันต้องการเพิ่มสตริงใหม่ลงในสตริงtestListเดียวหากไม่มีอยู่ในรายการ ดังนั้นฉันต้องทำการค้นหาตามตัวพิมพ์เล็กและรายการให้มีประสิทธิภาพ ฉันใช้ไม่ได้Containsเพราะนั่นไม่ได้คำนึงถึงตัวเรือน ฉันไม่ต้องการใช้ToUpper/ToLowerด้วยเหตุผลด้านประสิทธิภาพ ฉันเจอวิธีนี้ซึ่งใช้งานได้:

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

ใช้งานได้ แต่มันก็ตรงกับคำบางส่วน หากรายการมี "แพะ" ฉันไม่สามารถเพิ่ม "ข้าวโอ๊ต" เพราะอ้างว่า "ข้าวโอ๊ต" มีอยู่แล้วในรายการ มีวิธีในการค้นหารายการอย่างมีประสิทธิภาพในกรณีที่ไม่รู้สึกตัวพิมพ์เล็กหรือใหญ่ที่คำต้องตรงทั้งหมด? ขอบคุณ

คำตอบ:


180

แทนที่จะเป็น String.IndexOf ให้ใช้String.Equalsเพื่อให้แน่ใจว่าคุณไม่มีการจับคู่บางส่วน นอกจากนี้อย่าใช้ 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% บางทีคนอื่นสามารถยืนยัน / ปฏิเสธได้
Brap

8
ในฐานะที่เป็น. NET 2.0 สิ่งนี้สามารถทำได้อย่างง่ายดาย - ดูคำตอบของ shaxby ด้านล่าง
Joe

3
กระบวนการการอ้างอิงของวิธี shaxby (ที่มีการใช้งานมากเกินไปที่ใช้ IEqualityComparer) เป็นส่วนหนึ่งของ LINQ ดังนั้นจึงไม่สามารถใช้งานได้ตั้งแต่ NET 2.0 เพียงแค่คลาส StringComparer ได้รับรอบในขณะที่ รายการ <T> ไม่มีวิธีการนั้นและไม่มี ArrayList หรือ StringCollection (สิ่งที่เขาสามารถอ้างอิงได้ง่ายว่าเป็น 'รายการ')
Adam Sills

เพราะฉันต้องการดัชนีจริง ๆนี่คือคำตอบที่ดีที่สุดสำหรับฉัน
Nyerguds

1
วิธีแก้ปัญหาแรกควรใช้List<>.Exists(Predicate<>)วิธีการแบบอินสแตนซ์ นอกจากนี้โปรดทราบว่าหากรายการมีnullรายการสิ่งนี้สามารถระเบิดได้ ในกรณีนั้นมันจะปลอดภัยkeyword.Equals(x, StringComparison.OrdinalIgnoreCase)กว่าที่จะพูดมากกว่าx.Equals(keyword, StringComparison.OrdinalIgnoreCase)(ถ้าคุณสามารถรับประกันได้ว่าkeywordจะไม่มีโมฆะ)
Jeppe Stig Nielsen

360

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่ในกรณีที่คนอื่นกำลังมองหาคุณสามารถใช้Containsโดยการให้เปรียบเทียบความเท่าเทียมกันสตริงที่ไม่สำคัญกรณี:

using System.Linq;

// ...

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

นี้ได้รับมีตั้งแต่สุทธิ 2.0 ตามMSDN


21
คำตอบที่ดีที่สุดแน่นอนที่นี่ :)
Joe

22
<T> นับไม่ถ้วนเนื้อหา (สิ่งที่คุณอ้างอิง) ไม่ได้มีมาตั้งแต่. NET 2.0 ไม่มีรายการ <T> มีเนื้อหาที่มีการใช้งานเกินพิกัด
Adam Sills

@AdamSills ถูกต้อง ไม่มีวิธีการบรรจุในรายการ <T> และถ้ามันเป็นคอลเล็กชั่นสันหลังยาวกว่ามันสามารถทำซ้ำได้สองครั้งเหมือนวิธีอื่น ๆ ที่นับได้ อิมโฮวิธีนี้ไม่ควรใช้กับกรณีเช่นนี้เพราะมันไม่สมเหตุสมผลสำหรับกรณีนี้
Sergey Litvinov

40
ฉันไม่ได้เห็นโอเวอร์โหลดนี้ในตอนแรก แต่คุณต้องเพิ่มโดยใช้ System.Linq จากนั้นจะปรากฏขึ้น
Michael

1
StringComparerระดับได้รับรอบตั้งแต่ 2.0 แต่มีเกินพิกัดของที่ถูกนำมาใช้ใน 3.5 msdn.microsoft.com/en-us/library/bb339118(v=vs.110).aspx
เดนิส Skidmore

18

จากคำตอบของ Adam Sills ด้านบน - นี่เป็นวิธีส่วนขยายที่สะอาดดีสำหรับประกอบด้วย ... :)

///----------------------------------------------------------------------
/// <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" มิฉะนั้นคุณจะไม่เห็นว่าเกินพิกัดสำหรับ. Contents
Julian Melville

1

ขึ้นอยู่กับคำตอบของ Lance Larsen - นี่คือวิธีการขยายพร้อมกับสตริงที่แนะนำเปรียบเทียบกับสตริง

ขอแนะนำเป็นอย่างยิ่งให้คุณใช้งานเกินพิกัดของ String.Compare ที่ใช้พารามิเตอร์ StringComparison การโอเวอร์โหลดเหล่านี้ไม่เพียง แต่ช่วยให้คุณสามารถกำหนดพฤติกรรมการเปรียบเทียบที่แน่นอนที่คุณต้องการเท่านั้นการใช้สิ่งเหล่านี้จะทำให้โค้ดของคุณสามารถอ่านได้ง่ายขึ้นสำหรับนักพัฒนาอื่น ๆ [ Josh Free @ BCL Team Blog ]

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

ตอนนี้ "แพะ" และ "ข้าวโอ๊ต" จะไม่ตรงกัน แต่ "แพะ" และ "แพะ" จะ เพื่อหลีกเลี่ยงปัญหานี้คุณสามารถเปรียบเทียบความยาวของสองสาย

เพื่อหลีกเลี่ยงภาวะแทรกซ้อนทั้งหมดนี้คุณสามารถใช้พจนานุกรมแทนรายการ คีย์เหล่านี้จะเป็นสตริงตัวพิมพ์เล็กและค่าจะเป็นสตริงจริง วิธีนี้ประสิทธิภาพไม่เจ็บเพราะคุณไม่ต้องใช้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; }
}

หากคุณต้องการลบหนังสือที่มีคำหลักบางคำในคุณสมบัติข้อความคุณสามารถสร้างรายการคำหลักและลบออกจากรายการหนังสือ:

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
ผลิตรายการที่สองทำไม นั่นไม่ได้มีประสิทธิภาพมาก สำหรับ (var i = 0; i <itemsList.Count; i ++) {ถ้า (item.ToLower () == searchItem.ToLower ()) {return i}}
wesm

ฉันเดาว่าเราจะไม่มีทางรู้
Denny
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.