Case-insensitive Array#include?

后端 未结 6 1036
小蘑菇
小蘑菇 2020-12-13 12:35

I want to know what\'s the best way to make the String.include? methods ignore case. Currently I\'m doing the following. Any suggestions? Thanks!



        
相关标签:
6条回答
  • 2020-12-13 12:50
    class String
        def caseinclude?(x)
            a.downcase.include?(x.downcase)
        end
    end
    
    0 讨论(0)
  • 2020-12-13 12:52

    For an array, use:

    array.map(&:downcase).include?(string)
    

    Regexps are very slow and should be avoided.

    0 讨论(0)
  • 2020-12-13 12:52

    To farnoy in my case your example doesn't work for me. I'm actually looking to do this with a "substring" of any.

    Here's my test case.

    x = "<TD>", "<tr>", "<BODY>"
    y = "td"
    x.collect { |r| r.downcase }.include? y
    => false
    x[0].include? y
    => false
    x[0].downcase.include? y
    => true
    

    Your case works with an exact case-insensitive match.

    a = "TD", "tr", "BODY"
    b = "td"
    a.collect { |r| r.downcase }.include? b
    => true
    

    I'm still experimenting with the other suggestions here.

    ---EDIT INSERT AFTER HERE---

    I found the answer. Thanks to Drew Olsen

    var1 = "<TD>", "<tr>","<BODY>"
    => ["<TD>", "<tr>", "<BODY>"]
    var2 = "td"
    => "td"
    var1.find_all{|item| item.downcase.include?(var2)}
    => ["<TD>"]
    var1[0] = "<html>"
    => "<html>"
    var1.find_all{|item| item.downcase.include?(var2)}
    => []
    
    0 讨论(0)
  • 2020-12-13 12:53

    You can use casecmp to do your comparison, ignoring case.

    "abcdef".casecmp("abcde")     #=> 1
    "aBcDeF".casecmp("abcdef")    #=> 0
    "abcdef".casecmp("abcdefg")   #=> -1
    "abcdef".casecmp("ABCDEF")    #=> 0
    
    0 讨论(0)
  • 2020-12-13 13:03

    Summary

    If you are only going to test a single word against an array, or if the contents of your array changes frequently, the fastest answer is Aaron's:

    array.any?{ |s| s.casecmp(mystr)==0 }
    

    If you are going to test many words against a static array, it's far better to use a variation of farnoy's answer: create a copy of your array that has all-lowercase versions of your words, and use include?. (This assumes that you can spare the memory to create a mutated copy of your array.)

    # Do this once, or each time the array changes
    downcased = array.map(&:downcase)
    
    # Test lowercase words against that array
    downcased.include?( mystr.downcase )
    

    Even better, create a Set from your array.

    # Do this once, or each time the array changes
    downcased = Set.new array.map(&:downcase)
    
    # Test lowercase words against that array
    downcased.include?( mystr.downcase )
    

    My original answer below is a very poor performer and generally not appropriate.

    Benchmarks

    Following are benchmarks for looking for 1,000 words with random casing in an array of slightly over 100,000 words, where 500 of the words will be found and 500 will not.

    • The 'regex' text is my answer here, using any?.
    • The 'casecmp' test is Arron's answer, using any? from my comment.
    • The 'downarray' test is farnoy's answer, re-creating a new downcased array for each of the 1,000 tests.
    • The 'downonce' test is farnoy's answer, but pre-creating the lookup array once only.
    • The 'set_once' test is creating a Set from the array of downcased strings, once before testing.
                    user     system      total        real
    regex      18.710000   0.020000  18.730000 ( 18.725266)
    casecmp     5.160000   0.000000   5.160000 (  5.155496)
    downarray  16.760000   0.030000  16.790000 ( 16.809063)
    downonce    0.650000   0.000000   0.650000 (  0.643165)
    set_once    0.040000   0.000000   0.040000 (  0.038955)
    

    If you can create a single downcased copy of your array once to perform many lookups against, farnoy's answer is the best (assuming you must use an array). If you can create a Set, though, do that.

    If you like, examine the benchmarking code.


    Original Answer

    I (originally said that I) would personally create a case-insensitive regex (for a string literal) and use that:

    re = /\A#{Regexp.escape(str)}\z/i # Match exactly this string, no substrings
    all = array.grep(re)              # Find all matching strings…
    any = array.any?{ |s| s =~ re }   #  …or see if any matching string is present
    

    Using any? can be slightly faster than grep as it can exit the loop as soon as it finds a single match.

    0 讨论(0)
  • 2020-12-13 13:06

    my_array.map!{|c| c.downcase.strip}

    where map! changes my_array, map instead returns a new array.

    0 讨论(0)
提交回复
热议问题