简体   繁体   中英

Best Pattern to Indifferently Compare Strings/Symbols for Equality?

Is there an idiomatic Ruby pattern to test two 'strings' for equality that is indifferent to whether the operands are strings or symbols?

I'd like to use some operator to perform this comparison: :abc == 'abc'.to_sym without the need to normalize the operands to strings or symbols.

The HashWithIndifferentAccess behaviour in active_support is a pretty useful analogy for the sort of thing I'm looking for.

If you want to monkey patch the generic functionality in everywhere.

class Object
  def to_s_equals? var
    self.to_s == var
  end
end 

As mentioned, only convert symbols to strings, not strings to symbols unless you have a subsequent use for the symbol. You could be more specific and only do that on Symbol

Alternatively you could add something for String and Symbols, but I can't think of a good common name.

class Symbol
  def equals_string? var
    self.to_s == var
  end
end 

class String
  def equals_symbol? var
    self == var.to_s
  end
end

Even then equals isn't quite right, but match infers a regex. homologous maybe? (corresponding in structure, but not necessarily function)

I don't think your getting much brevity on to_s == . Maybe a bit of clarity enforcing the order you do the comparisons in.

Since Ruby 2.4 You can use match? method

> :abc.match?(/abc/)
=> true
> 'abc'.match?(/abc/)
=> true

You can use regex pattern to do this comparisons :-

/\Aabc\z/ === "abc" # => true
/\Aabc\z/ === :abc # => true

So, you can make your own method :-

def comparisons(sym, str, patt)
   [sym, str].all? { |i| patt === i }
end
comparisons(:abc, "abc", /\Aabc\z/) # => true
comparisons(:abc, "abcd", /\Aabc\z/) # => false
comparisons(:abcd, "abc", /\Aabc\z/) # => false

HashWithIndifferentAccess explicitly tests is_a?(Symbol) and, if so, converts it to a string.

Side note, converting a symbol to a string is probably better practice than the other direction. Strings are subject to cleanup and symbols are not, so symbols will hang around forever and slowly leak memory.

You could also patch into Symbol if you know that the symbol will always be used as the receiver. Such as

 class Symbol
    alias_method :old_match, :=~
    def =~(s)
      old_match(s.is_a?(String) ? /#{Regexp.quote(s)}/ : s)
    end
 end

 :abc =~ "abc"
 #=> 0
 :abc =~ /abc/
 #=> 0
 :abc =~ "abcd"
 #=> nil

This will work the exact same way as :abc =~ currently works but with a transformation of a String to a Regex first.

Although I generally suggest against monkey patching built in classes unless the implementation is isolated this should not have too many foreseeable side effects

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM