How to Create a Hash in Ruby That Compares Strings, Ignoring Case

How do I create a hash in Ruby that compares strings, ignoring case?

To prevent this change from completely breaking independent parts of your program (such as other ruby gems you are using), make a separate class for your insensitive hash.

class HashClod < Hash
def [](key)
super _insensitive(key)
end

def []=(key, value)
super _insensitive(key), value
end

# Keeping it DRY.
protected

def _insensitive(key)
key.respond_to?(:upcase) ? key.upcase : key
end
end

you_insensitive = HashClod.new

you_insensitive['clod'] = 1
puts you_insensitive['cLoD'] # => 1

you_insensitive['CLod'] = 5
puts you_insensitive['clod'] # => 5

After overriding the assignment and retrieval functions, it's pretty much cake. Creating a full replacement for Hash would require being more meticulous about handling the aliases and other functions (for example, #has_key? and #store) needed for a complete implementation. The pattern above can easily be extended to all these related methods.

Compare two hashes no matter symbols or strings, rails

How about this?

require 'set'

def sorta_equal?(sym_hash, str_hash)
return false unless sym_hash.size == str_hash.size
sym_hash.to_a.to_set == str_hash.map { |pair|
pair.map { |o| o.is_a?(String) ? o.to_sym : o } }.to_set
end

sym_hash= {:id=>58, :locale=>:"en-US"}

sorta_equal?(sym_hash, {"id"=>58, "locale"=>"en-US"}) #=> true
sorta_equal?(sym_hash, {"locale"=>"en-US", "id"=>58 }) #=> true
sorta_equal?(sym_hash, {"id"=>58, "local"=>"en-US", "a"=>"b" }) #=> false
sorta_equal?(sym_hash, {"id"=>58, "lacole"=>"en-US"}) #=> false
sorta_equal?(sym_hash, {"id"=>58, [1,2,3]=>"en-US"}) #=> false
sorta_equal?({}, {}) #=> true

class A; end
a = A.new
sorta_equal?({:id=>a, :local=>:b}, {"id"=>a, "local"=>"b"}) #=> true

Convert hash keys to lowercase -- Ruby Beginner

You can use something like this:

CSV.foreach(file, :headers => true) do |row|
new_hash = {}
row.to_hash.each_pair do |k,v|
new_hash.merge!({k.downcase => v})
end

Users.create!(new_hash)
end

I had no time to test it but, you can take idea of it.

Hope it will help

String values as hash keys, copy created

From Hash.[]= documentation:

key should not have its value changed while it is in use as a key (an
unfrozen String passed as a key will be duplicated and frozen).

Since by default, strings are not immutable in ruby, theoretically you can change them after you set them as keys in your hash. If you do that - your hash will become invalid, as it will not be able to find those keys properly.

Since string are ubiquitous and are often used by reference, this way Ruby protects its hashes from unexpected bugs, which are very hard to detect.

Strings that compare equal don't find same objects in Hash

This was a bug in ruby < 2.1.3

$ rvm use 2.1.2
Using /Users/richniles/.rvm/gems/ruby-2.1.2
$ irb
2.1.2 :001 > context = "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ"
=> "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ"
2.1.2 :002 > slice_str = context.slice(105,24) # => "http://cnnmon.ie/1kcFZSQ"
=> "http://cnnmon.ie/1kcFZSQ"
2.1.2 :003 > str = "http://cnnmon.ie/1kcFZSQ"
=> "http://cnnmon.ie/1kcFZSQ"
2.1.2 :004 > redirects = {"http://cnnmon.ie/1kcFZSQ"=>""}
=> {"http://cnnmon.ie/1kcFZSQ"=>""}
2.1.2 :005 > redirects.key?(slice_str)
=> false
2.1.2 :006 > redirects.key?(str)
=> true

but do the same in ruby 2.1.3:

 $ rvm use 2.1.3
Using /Users/richniles/.rvm/gems/ruby-2.1.3
$ irb
2.1.3 :001 > context = "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ"
=> "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ"
2.1.3 :002 > slice_str = context.slice(105,24) # => "http://cnnmon.ie/1kcFZSQ"
=> "http://cnnmon.ie/1kcFZSQ"
2.1.3 :003 > str = "http://cnnmon.ie/1kcFZSQ"
=> "http://cnnmon.ie/1kcFZSQ"
2.1.3 :004 > redirects = {"http://cnnmon.ie/1kcFZSQ"=>""}
=> {"http://cnnmon.ie/1kcFZSQ"=>""}
2.1.3 :005 > redirects.key?(slice_str)
=> true
2.1.3 :006 > redirects.key?(str)
=> true

How do I compare two hashes?

You can compare hashes directly for equality:

hash1 = {'a' => 1, 'b' => 2}
hash2 = {'a' => 1, 'b' => 2}
hash3 = {'a' => 1, 'b' => 2, 'c' => 3}

hash1 == hash2 # => true
hash1 == hash3 # => false

hash1.to_a == hash2.to_a # => true
hash1.to_a == hash3.to_a # => false


You can convert the hashes to arrays, then get their difference:

hash3.to_a - hash1.to_a # => [["c", 3]]

if (hash3.size > hash1.size)
difference = hash3.to_a - hash1.to_a
else
difference = hash1.to_a - hash3.to_a
end
Hash[*difference.flatten] # => {"c"=>3}

Simplifying further:

Assigning difference via a ternary structure:

  difference = (hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
=> [["c", 3]]
Hash[*difference.flatten]
=> {"c"=>3}

Doing it all in one operation and getting rid of the difference variable:

  Hash[*(
(hash3.size > hash1.size) \
? hash3.to_a - hash1.to_a \
: hash1.to_a - hash3.to_a
).flatten]
=> {"c"=>3}

How can I make hash key lookup case-insensitive?

You will have to use a tied hash. For example Hash::Case::Preserve.

How do I alphabetize an array ignoring case?

How about:

wordlist.sort_by { |word| word.downcase }

Or even shorter:

wordlist.sort_by(&:downcase)


Related Topics



Leave a reply



Submit