What is the meaning of grep on a Hash? - ruby

{'a' => 'b'}.grep /a/
=> []
>> {'a' => 'b'}.grep /b/
=> []
It doesn't seem to match the keys or values. Does it do something I'm not discerning?

grep is defined on Enumerable, i.e. it is a generic method that doesn't know anything about Hashes. It operates on whatever the elements of the Enumerable are. Ruby doesn't have a type for key-value-pairs, it simply represents Hash entries as two-element arrays where the first element is the key and the second element is the value.
grep uses the === method to filter out elements. And since neither
/a/ === ['a', 'b']
nor
/b/ === ['a', 'b']
are true, you always get an empty array as response.
Try this:
def (t = Object.new).===(other)
true
end
{'a' => 'b'}.grep t
# => [['a', 'b']]
Here you can see how grep works with Hashes.

Related

Function Parameters to Hash Symbols for Rendering Partials

I have a lot of Render Helper functions like
def generic_form_datetime_field(f,attribute_name, my_label ,selected_model_instance)
render(:partial => 'common_partials/generic_form/datetime_field',
:locals => {:f => f,
:attribute_name => attribute_name,
:my_label => my_label,
:selected_model_instance => selected_model_instance
})
end
notice the local field is full of duplicate keys and values from function parameters. What is the best practice around this? Is there a way to get the parameters from the method and put them as keys for the hash?
taking hash arguments
You can use a few different syntaxes to pull in named arguments as a hash. One is using the "double splat":
def func(**args)
puts args
end
func(a: 'aval', b: 3) # prints {:a=>"aval", :b=>3}
Another syntax you will see is:
def func(opts={})
puts opts
end
func(a: 'aval', b: 3) # prints: {:a=>"aval", :b=>3}
More info can be found in this blog post.
filtering the hash
If you want to be defensive, you can use a function that filters collections in general: select. Here's an example of how to only take values from a specified whitelist for hash keys:
h = {a: 'a', b: 'b', c: 'c'}
whitelist = [:a, :c]
h.select { |k, _| whitelist.include?(k) }
# result: {:a=>"a", :c=>"c"}

Create hash with keys from array and a standard value

I have an array like this:
['a', 'b', 'c']
What is the simplest way to turn it into:
{'a' => true, 'b' => true, 'c' => true}
true is just a standard value that values should hold.
How about below ?
2.1.0 :001 > ['a', 'b', 'c'].each_with_object(true).to_h
=> {"a"=>true, "b"=>true, "c"=>true}
Try:
Hash[ary.map {|k| [k, true]}]
Since Ruby 2.0 you can use to_h method:
ary.map {|k| [k, true]}.to_h
Depending on your specific needs, maybe you do not actually need to initialize the values. You could simply create a Hash with a default value of true this way:
h = Hash.new(true)
#=> {}
Then, when you try to access a key that was not present before:
h['a']
#=> true
h['b']
#=> true
Pros: less memory used, faster to initialize.
Cons: does not actually store keys so the hash will be empty until some other code stores values in it. This will only be a problem if your program relies on reading the keys from the hash or wants to iterate over the hash.
['a', 'b', 'c'].each_with_object({}) { |key, hash| hash[key] = true }
Another one
> Hash[arr.zip Array.new(arr.size, true)]
# => {"a"=>true, "b"=>true, "c"=>true}
You can also use Array#product:
['a', 'b', 'c'].product([true]).to_h
#=> {"a"=>true, "b"=>true, "c"=>true}
Following code will do this:
hash = {}
['a', 'b', 'c'].each{|i| hash[i] = true}
Hope this helps :)
If you switch to Python, it's this easy:
>>> l = ['a', 'b', 'c']
>>> d = dict.fromkeys(l, True)
>>> d
{'a': True, 'c': True, 'b': True}

Getting an array of hash values given specific keys

Given certain keys, I want to get an array of values from a hash (in the order I gave the keys). I had done this:
class Hash
def values_for_keys(*keys_requested)
result = []
keys_requested.each do |key|
result << self[key]
end
return result
end
end
I modified the Hash class because I do plan to use it almost everywhere in my code.
But I don't really like the idea of modifying a core class. Is there a builtin solution instead? (couldn't find any, so I had to write this).
You should be able to use values_at:
values_at(key, ...) → array
Return an array containing the values associated with the given keys. Also see Hash.select.
h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
h.values_at("cow", "cat") #=> ["bovine", "feline"]
The documentation doesn't specifically say anything about the order of the returned array but:
The example implies that the array will match the key order.
The standard implementation does things in the right order.
There's no other sensible way for the method to behave.
For example:
>> h = { :a => 'a', :b => 'b', :c => 'c' }
=> {:a=>"a", :b=>"b", :c=>"c"}
>> h.values_at(:c, :a)
=> ["c", "a"]
i will suggest you do this:
your_hash.select{|key,value| given_keys.include?(key)}.values

Conditional key/value in a ruby hash

Is there a nice (one line) way of writing a hash in ruby with some entry only there if a condition is fulfilled? I thought of
{:a => 'a', :b => ('b' if condition)}
But that leaves :b == nil if the condition is not fulfilled. I realize this could be done easily in two lines or so, but it would be much nicer in one line (e.g. when passing the hash to a function).
Am I missing (yet) another one of ruby's amazing features here? ;)
UPDATE Ruby 2.4+
Since ruby 2.4.0, you can use the compact method:
{ a: 'a', b: ('b' if cond) }.compact
Original answer (Ruby 1.9.2)
You could first create the hash with key => nil for when the condition is not met, and then delete those pairs where the value is nil. For example:
{ :a => 'a', :b => ('b' if cond) }.delete_if{ |k,v| v.nil? }
yields, for cond == true:
{:b=>"b", :a=>"a"}
and for cond == false
{:a=>"a"}
UPDATE for ruby 1.9.3
This is equivalent - a bit more concise and in ruby 1.9.3 notation:
{ a: 'a', b: ('b' if cond) }.reject{ |k,v| v.nil? }
From Ruby 1.9+, if you want to build a hash based on conditionals you can use tap, which is my new favourite thing. This breaks it onto multiple lines but is more readable IMHO:
{}.tap do |my_hash|
my_hash[:a] = 'a'
my_hash[:b] = 'b' if condition
end
>= Ruby 2.4:
{a: 'asd', b: nil}.compact
=> {:a=>"asd"}
Interested in seeing other answers, but this is the best I can think up of for a one-liner (I'm also notoriously bad at one-liners :P)
{:a => 'a'}.merge( condition ? {:b => 'b'} : {} )
There's a lot of clever solutions in here, but IMO the simplest and therefore best approach is
hash = { a: 'a', b: 'b' }
hash[:c] = 'c' if condition
It goes against the OP's request of doing it in two lines, but really so do the other answers that only appear to be one-liners. Let's face it, this is the most trivial solution and it's easy to read.
In Ruby 2.0 there is a double-splat operator (**) for hashes (and keyword parameters) by analogy to the old splat operator (*) for arrays (and positional parameters). So you could say:
{a: 'b', **(condition ? {b: 'b'} : {})}
Hash[:a, 'a', *([:b, 'b'] if condition1), *([:c, 'c'] if condition2)]
This relies on the fact that *nil expands to vacuity in ruby 1.9. In ruby 1.8, you might need to do:
Hash[:a, 'a', *(condition1 ? [:b, 'b'] : []), *(condition2 ? [:c, 'c'] : [])]
or
Hash[:a, 'a', *([:b, 'b'] if condition1).to_a, *([:c, 'c'] if condition2).to_a]
If you have multiple conditions and logic that others will need to understand later then I suggest this is not a good candidate for a 1 liner. It would make more sense to properly create your hash based on the required logic.
This one is nice for multiple conditionals.
(
hash = {:a => 'a'}.tap {|h|
h.store( *[(:b if condition_b), 'b'] )
h.store( *[(:c if condition_c), 'c'] )
}
).delete(nil)
Note that I chose nil as the "garbage" key, which gets deleted when you're done. If you ever need to store a real value with a nil key, just change the store conditionals to something like:
(condition_b ? :b : garbage_key)
then delete(garbage_key) at the end.
This solution will also keep existing nil values intact, e.g. if you had :a => nil in the original hash, it won't be deleted.
My one-liner solution:
{:a => 'a'}.tap { |h| h.merge!(:b => 'b') if condition }
hash, hash_new = {:a => ['a', true], :b => ['b', false]}, {}
hash.each_pair{|k,v| hash_new[k] = v[1] ? v : nil }
puts hash_new
eval("{:a => 'a' #{', :b => \'b\'' if condition }}")
or even
eval("{#{[":a => 'a'", (":b=>'b'" if ax)].compact.join(',')}}")
for more simple add conditions

Ruby value of a hash key?

I've got a list of values that are in a Ruby hash. Is there a way to check the value of the key and if it equals "X", then do "Y"?
I can test to see if the hash has a key using hash.has_key?, but now I need to know if hash.key == "X" then...?
Hashes are indexed using the square brackets ([]). Just as arrays. But instead of indexing with the numerical index, hashes are indexed using either the string literal you used for the key, or the symbol.
So if your hash is similar to
hash = { "key1" => "value1", "key2" => "value2" }
you can access the value with
hash["key1"]
or for
hash = { :key1 => "value1", :key2 => "value2"}
or the new format supported in Ruby 1.9
hash = { key1: "value1", key2: "value2" }
you can access the value with
hash[:key1]
This question seems to be ambiguous.
I'll try with my interpretation of the request.
def do_something(data)
puts "Found! #{data}"
end
a = { 'x' => 'test', 'y' => 'foo', 'z' => 'bar' }
a.each { |key,value| do_something(value) if key == 'x' }
This will loop over all the key,value pairs and do something only if the key is 'x'.
As an addition to e.g. #Intrepidd s answer, in certain situations you want to use fetch instead of []. For fetch not to throw an exception when the key is not found, pass it a default value.
puts "ok" if hash.fetch('key', nil) == 'X'
Reference: https://docs.ruby-lang.org/en/2.3.0/Hash.html .
How about this?
puts "ok" if hash_variable["key"] == "X"
You can access hash values with the [] operator
It seems that your question is maybe a bit ambiguous.
If “values” in the first sentence means any generic value (i.e. object, since everything in Ruby can be viewed as an object), then one of the other answers probably tells you what you need to know (i.e. use Hash#[] (e.g. hash[some_key]) to find the value associated with a key).
If, however, “values” in first sentence is taken to mean the value part of the “key, value pairs” (as are stored in hashes), then your question seems like it might be about working in the other direction (key for a given value).
You can find a key that leads to a certain value with Hash#key.
ruby-1.9.2-head :001 > hash = { :a => '1', :b => :two, :c => 3, 'bee' => :two }
=> {:a=>"1", :b=>:two, :c=>3, "bee"=>:two}
ruby-1.9.2-head :002 > a_value = :two
=> :two
ruby-1.9.2-head :003 > hash.key(a_value)
=> :b
If you are using a Ruby earlier than 1.9, you can use Hash#index.
When there are multiple keys with the desired value, the method will only return one of them. If you want all the keys with a given value, you may have to iterate a bit:
ruby-1.9.2-head :004 > hash[:b] == hash['bee']
=> true
ruby-1.9.2-head :005 > keys = hash.inject([]) do # all keys with value a_value
ruby-1.9.2-head :006 > |l,kv| kv[1] == a_value ? l << kv[0] : l
ruby-1.9.2-head :007?> end
=> [:b, "bee"]
Once you have a key (the keys) that lead to the value, you can compare them and act on them with if/unless/case expressions, custom methods that take blocks, et cetera. Just how you compare them depends on the kind of objects you are using for keys (people often use strings and symbols, but Ruby hashes can use any kind of object as keys (as long as they are not modified while they serve as keys)).
I didn't understand your problem clearly but I think this is what you're looking for(Based on my understanding)
person = {"name"=>"BillGates", "company_name"=>"Microsoft", "position"=>"Chairman"}
person.delete_if {|key, value| key == "name"} #doing something if the key == "something"
Output: {"company_name"=>"Microsoft", "position"=>"Chairman"}

Resources