Match value if other are not matched - ruby

I have this Ruby map:
FIXED_COUNTRY_TO_PHONE = {
'FI' => '+358501111',
'RU' => '4019900780',
'SE' => '+4672345678',
'UA' => '0123456789',
'KZ' => '0123456789'
}.freeze
How I can set some final value for example '*' => '1234567' if the other values are not matching?

FIXED_COUNTRY_TO_PHONE = Hash.new('1234567').merge({
'FI' => '+358501111',
'RU' => '4019900780',
'SE' => '+4672345678',
'UA' => '0123456789',
'KZ' => '0123456789'
}).freeze
But simple
DEFAULT = "1234567".freeze
FIXED_COUNTRY_TO_PHONE["FI"] || DEFAULT
#=> "+358501111"
FIXED_COUNTRY_TO_PHONE["??"] || DEFAULT
#=> "1234567"
Looks also nice

You can use Hash#default= to set the default value:
hash = {
'FI' => '+358501111',
'RU' => '4019900780',
'SE' => '+4672345678',
'UA' => '0123456789',
'KZ' => '0123456789'
}
hash.default = '1234567'
hash['UK']
#=> '1234567'

There are two main ways of dealing with this problem. #llya assigns a default value to the hash, #fl00r applies the default when the hash is evaluated for a non-existing key, causing nil to be returned.
llya shows one way of implementing the default value. It is very clear but uses a few lines of code. Two other ways of doing that deserve mention (though I am not suggesting they are in any sense better than how llya has done it).
Set the default then add the key-value pairs:
hash = Hash.new('1234567').
merge('FI'=>'+358501111', 'RU'=>'4019900780', 'SE'=>'+4672345678',
'UA'=>'0123456789', 'KZ'=>'0123456789').
freeze
hash['oh, my']
#=> "1234567"
Use Object#tap
hash = { 'FI'=>'+358501111', 'RU'=>'4019900780', 'SE'=>'+4672345678',
'UA'=>'0123456789', 'KZ'=>'0123456789'
}.tap { |h| h.default = '1234567' }.
freeze
hash['oh, my']
#=> "1234567"
A variantof fl00r's || DEFALULT approach, which arguably reads better, is to use Hash#fetch:
hash = { 'FI'=>'+358501111', 'RU'=>'4019900780', 'SE'=>'+4672345678',
'UA'=>'0123456789', 'KZ'=>'0123456789' }
DEFAULT = "1234567"
hash.fetch('oh, my', DEFAULT)
#=> "1234567"
hash[key] || DEFAULT clearly cannot be used if hash has a key whose value may be nil or false, though that is not an issue here.
Both approaches have pros and cons. Setting a default value avoids the need for the retrieval of values (which may be done in multiple places) to be concerned about whether the hash contains a particular key, but on the other hand readers of those parts of the code may not be aware (or have forgotten) that the hash was defined with a default value. If the || DEFAULT approach is used there is the possibility that the coder may forget to include || DEFAULT in one or more places where the hash is referenced. In terms of code maintenance I would think it's a toss-up, provided the constant DEFAULT is defined in close proximity to the hash, and commented suitably.
It seems to me that it's generally best to use a default value, and wherever the hash is referenced remind readers that it has a default value, either with a comment or a suggestive name for the hash (e.g., applies_to_oranges_with_default).

Related

How to use a string to find a symbol in a hash

I have a string:
category = "cupcakes"
I need to see if this hash has that string, converted to a symbol, as a key:
products = {
:cupcakes => [{:title => "chocolate", :flavor => "chocolate"}, {:title => "summer special", :flavor => "pink cake"}],
:cookies => [{:title => "chocolate bliss", :flavor => "chocolate chip"}, {:title => "breakfast surprise", :flavor => "oatmeal raisin"}]
}
This one does not work because category is a string, not a symbol.
products.key?(category)
This one does not work because :cupcake is a unique symbol, and we are just creating another symbol.
products.key?(category.to_sym)
I think this comes from a misunderstanding of how symbols work. Every symbol with the same content is absolutely identical down to having the same object_id. Symbols with different content are always different. This is in contrast to strings where they may have identical content but a different object_id, or where one moment a string might contain one thing and the next it's completely different despite having the same object_id.
Ruby's object_id values are just what they say they are, "object identifiers", and are a way of saying "object number".
Now the string "cupcake" and the symbol :cupcake are not the same:
"cupcake" == :cupcake
# => false
If you do the conversion, they are:
"cupcake".to_sym == :cupcake
# => true
The real problem is you're expecting :cupcake and :cupcakes to be the same thing. Ruby doesn't understand English rules and semantics, so as far as its concerned those are two completely different things.
If you're using ActiveSupport you can do things like:
"cupcake".pluralize.to_sym == :cupcakes
# => true
That requires an additional library, but the good news is it comes with things like Rails by default.
In any case, the way you've defined your data you need to reference it as something like:
products[:cupcakes]
products["cupcake".to_sym]
products["cupcake".pluralize.to_sym]

Ruby change value in hash by reference

I'm using eval to work with a hash. This part works:
some_hash = {"a" => {"b" => "c"}}
target_hash = "some_hash"
target_key = "['a']"
my_value = eval(target_hash + target_key)
puts "my_value " + my_value.to_s
and prints:
my_value {"b"=>"c"}
How would I change the value, using eval, so that the hash results in this:
some_hash = {"a" => {"d" => "e"}}
Thanks
Edit:
I don't think I'm explaining correctly. I have to drill down into a hash, but I want the flexibility to do it with a string that is set at run time. That string could be "['key_level_1']['key_level_2']['key_level_3']" which is supposed to refer to some_hash['key_level_1']['key_level_2']['key_level_3'].
And again, i need to set that value to something. Does that make sense?
I would take an array e.g. ['key1', 'key2', 'key3'] (which can be constructed from an appropriately formatted string) and the "root object" and use it to locate a particular "target object" branch (I would recommend recursion). Then manipulate the appropriate object that was located. In this case some_hash should be the "root object" to start the traversal from, and not a string.
Here is a link to a really old answer I wrote (when I still actively used ruby). It doesn't handle the assignment bit, but I believe it shows the start of a valid approach that is eval-free: How do you access nested elements of a hash with a single string key? After the "target object" is located, then it's just a matter of assigning a new value to particular key. (The accepted answer in the linked post is also a gem, if not a little more cryptic and symbol-leaking.)
Happy coding.
You can use Hash#replace
def changeHash(a, b, targetHash, targetKey)
change = ".replace({'#{a}' => '#{b}'})"
my_value = eval(target_hash + target_key + change)
end
some_hash = {"a" => {"b" => "c"}}
target_key = "['a']"
changeHash('d', 'e', 'some_hash', '["a"]')
I suggest setting up a method like this:
def hash_change(target_hash,target_key,*new_value)
if new_value
target_hash["target_key"] = new_value
end
puts "my_value " + target_hash["target_key"]
end
That will give you flexibility to either display the original or a new hash should you pass in a new value.
Edit: sorry forgot the call to the method
hash_change(some_hash,"a",{"d" => "e"})
Thanks all, for the help. I changed it up a bit. Instead of passing in string as array, I just pass in an object like this:
{some_hash => {"a" => {"b" => "nil"}}}
Then I recursively traverse both the reference, and the real hash. When I detect nil in the reference, I know I am there.

Validate hash keys/values

I am trying to validate the values of certain keys in a hash:
response[:payment_status] == 'Completed' && response[:receiver_email] == 'test#example.com' && response[:foo] == 'Bar'
While the above approach works I am quite sure there is a more elegant solution. I would hate to have a really long line if I add extra keys/values I want to validate.
P.S: I should mention I want a simple true/false returned.
You could create a hash of the expect key/values and then map the input hash values:
expected = {'payment_status' => 'Completed', 'receiver_email' => 'test#example.com' ... }
valid = expected.keys.all? {|key| response[key] == expected[key]}
This might help you out with validating hashes using another hash to define the structure/types that you expect (also works with nested hashes):
https://github.com/JamesBrooks/hash_validator
If you want to test equality on each element, there is an elegant solution:
response.slice(:payment_status, :receiver_email, :foo) == { :payment_status => 'Completed', :receiver_email => 'test#example.com', :foo => 'Bar'}
In my case, I need to compare inequality too. It can be done by calling BasicObject#instance_eval.
Then it writes:
response.instance_eval{ |hash| hash[:payment_status] != 'Pending' && hash[:receiver_email] == 'test#example.com' && hash[:boolean_result] }

Ruby => operator... eg: :text => /Log In/

What does this do? I find the example here, but other than what it does, what does it mean? I can't find anything on google because well, I am not sure what '=>' is even called in this context.
More examples here:
http://mechanize.rubyforge.org/mechanize/EXAMPLES_rdoc.html
In :text => /Log In/, you are passing a hash to page's link_with function, and the key in the hash is :text and its corresponding value is /Log In/.
Basically: :x => y means that :x is a key in a hash that maps to a value of y.
passing hashes to functions like this allows you to have something like (but not exactly) named parameters.
UPDATE:
A symbol of the form :something is called.... a symbol! You can think of them sort of like global string constants (but they're not quite the same). Now, when you think back to something like:
login_page.form_with(:action => '/account/login.php')
What you're actually doing is constructing a new hash on the fly. You create a new element in the hash, where the key is a string with the value "action", and the value of that element is "/account/login.php" (in this case, also a string, but I'm pretty sure you can store other things in hashes besides strings).
...whew! It's been a while since I've worked with Ruby. I hope I got that all correct. ;)
Some good looking pages here (more can be found by searching google for "ruby symbol")
http://glu.ttono.us/articles/2005/08/19/understanding-ruby-symbols
http://www.troubleshooters.com/codecorn/ruby/symbols.htm#_What_are_symbols
It associates a value with an index for hashes.
obj.method :text => /Log In/
is shorthand for
obj.method {:text => /Log In/}
It's used to create a hash expression, as in { key => value }.
Also, when used as the last parameter in a method call, the { } are not needed, so the key => value can appear alone.
>> p({:a => 1, :b => 2})
{:a=>1, :b=>2}
=> nil
>> p :c=>3, :d=>4
{:d=>4, :c=>3}
=> nil
>> t = { :e=>5, :f=>6 }
=> {:f=>6, :e=>5}
This shorthand is really nice in poetry mode, where a literal hash following a method name would look like a block.

Testing for collection index-types (ie arguments to [] method)

What's an efficient, rubyesque way of testing if a collection supports string indexing?
Long version:
I'd like for a class of mine to normalize certain values. To achieve this, an instance takes a collection of values as its 'values' attribute. I'd like values= to accept both lists (integer indexed collections, including the built-in Array) and associative arrays (object indexed collections, such as Hash). If passed a list, it turns the list into an associative array by inverting keys & values. This means the method needs to distinguish lists from associative arrays. It should also work on any indexed collection, not just descendants of Array and Hash, so any sort of type sniffing the collection type is considered ugly and wrong. Type sniffing the index type, however...
Currently, I'm using exceptions to tell the difference, but I prefer to use exceptions for, well, exceptional circumstances rather than a general control structure. It's just a personal preference, one I'm not too attached to. If exceptions are the ruby way to solve this problem, please let me know.
def values=(values)
begin
values['']
#values = values.dup
rescue TypeError
#values = Hash[ values.zip((0..values.length-1).to_a) ]
end
#values.each_value { |v| #values[v] = v}
end
Note: a complete solution would take the transitive closure of values, but for now I can assume the keys & values of values are from different domains.
The point of all this is to enable code like:
toggle.values = [:off, :on]
toggle.normalise(:off) == 0
toggle.normalise(1) == 1
bool.values = {:off => 0, :false => 0, :no => 0,
:on => 1, :true => 1, :yes => 1}
bool.normalise(:yes) == 1
bool.normalise(0) == 0
PS. This is for a personal project, so elegance and the Ruby way are paramount. I'm looking for interesting approaches, especially if they illustrate an interesting concept (such as "exceptions can be used as behavioral tests").
Duck typing to the rescue!
hash = if collection.respond_to? :to_hash
collection.to_hash
elsif collection.respond_to? :to_ary
collection.to_ary.inject({}) { |_hash,(key,value)| _hash.merge!(key => value) }
else if collection.respond_to? :inject
collection.inject({}) { |_hash,(key,value)| _hash.merge!(key => value) }
else
raise ArgumentError, "not a collection type I understand"
end
if want_dupe and collection.object_id == hash.object_id
hash = hash.dup
end

Resources