Validate hash keys/values - ruby

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] }

Related

Combine the values of two hash keys in an array of hashes

I have an array of hashes:
a = [{"ID"=>"FOO", "Type"=>"Name"}, {"ID"=>"1234", "Type"=>"CID"}]
I'm trying to extract the hash where the Type=='CID', then combine the two values to result in CID=1234.
I can do this in multiple steps:
h = a.find{|x| x['Type']=='CID'}
# => {"ID"=>"1234", "Type"=>"CID"}
"#{h['Type']}=#{h['ID']}"
# => "CID=1234"
Is there a way to do this in a one liner?
a.find { |h| h["Type"] == "CID" }&.values_at("Type", "ID")&.join("=")
#=>"CID=1234"
a.find { |h| h["Type"] == "cat" }&.values_at("Type", "ID")&.join("=")
#=> nil
& is Ruby's safe navigation operator, which made it's debut in Ruby v2.3. I added it to cause nil to be returned if there is no match on h["Type"].
You can do it in one line using:
a.select{|x| x['Type']=='CID'}
.map{|x| "type=#{x['Type']},id=#{x['ID']}"}[0]
You may try this:
if we are not having multiple values of Type = "CID":
a.select{|x| x["Type"] == "CID"}.map{|x| x.values_at("Type", "ID")}.join("=")
if we have are having Type="CID"
a.detect{|x| x["Type"]=="CID"}.values_at("Type", "ID").join("=")
If we don't have Type="CID" in above array will throw an error, be cautious.
Need to work in all cases we need to do:
a.detect{|x| x["Type"]=="CID"}.values_at("Type", "ID").join("=") if a.detect{|x| x["Type"]=="CID"}

Match value if other are not matched

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).

Ruby filtering array of hash

I have the following array of arguments that has a hash with the args keys and their relevant passed values:
args_array = [{:collection=>["abe", "<mus>", "hest"], :include_blank=>true, :required=>true}]
I need to filter this hash to just get only some keys (like :include_blank and :required) in this example, with their relevant values like so:
filtered_args_hash = {:include_blank=>true, :required=>true}
So the collection arg is excluded, and just the include_blank, and required in which I specified are the ones returned.
Update:
Here is a keys array of symbols in which I need to filter according to them:
filter_keys = %i(include_blank required)
How I can do that?
One way is to use Hash#keep_if:
args_array[0].dup.keep_if {|k| filter_keys.include? k}
# => {:include_blank=>true, :required=>true}
Note that .dup is here to prevent args_array from being modified, don't use it if you do want args_array to be updated.
The following should do solve your problem:
args_array.collect {|a| a if a[:include_blank] == true && a[:required=>true] == true }.compact
or
dup_list = args_array.dup
dup_list.delete_if {|a| !(a[:include_blank] == true && a[:required=>true] == true) }
args_array.map do |elem|
elem.select do |k, _|
%i(include_blank required).include? k
end
end
You tagged as Rails so you can use Hash#slice:
args_array[0].slice *filter_keys
# => {:include_blank=>true, :required=>true}

How to check if a key in a list of hash's contains a string in Ruby?

I have an array which consists of hash's:
people = [{'name':'Bob','id':12}, {'name':'Sam','id':25}, ...etc]
Is there any easy way to check if the array people contains a hash which contains an id 16 for example?
If this can be done with another data structure, please suggest it. I'm not stubborn on using a hash. I just need to store name and id (which may expand to more fields later).
I come from a Java/C background, if that helps in your explanation.
Something like this?
people.select { |p| p[:id] == '16' }
select will iterate thru the array and return the results
Also, you can detect to only get the first match
Enumerable#find, Enumerable#find_all and Enumerable#any? are good way to go as shown below :
people = [{name:'Bob',id:'12'}, {name:'Sam',id:'25'}]
p people.find{ |i| i[:id] == '12' } # to find a single and first entry which satisfies the given condtion
# => {:name=>"Bob", :id=>"12"}
people = [{name:'Bob',id:'12'}, {name:'Sam',id:'25'},{name:'Max',id:'12'}]
p people.find_all{ |i| i[:id] == '12' } # to find a multiple entries which satisfies the given condtion
# => [{:name=>"Bob", :id=>"12"}, {:name=>"Max", :id=>"12"}]
people = [{name:'Bob',id:'12'}, {name:'Sam',id:'25'},{name:'Max',id:'12'}]
p people.any? { |i| i[:id] == '12' }
# => true

How i can sort this hash by created of and key ,value pair

Hi i am little struggle to make this hash and sort it by created of and key , value pair.
Here is my code
hash_answers = {}
unless answers.blank?
answers.each_with_index do |ans ,index|
voted_up_users = ans.votes_up_by_all_users(ans)
voted_down_users = ans.votes_down_by_all_users(ans)
hash_answers[ans.id] = voted_up_users.count -voted_down_users.count #line one
hash_answers[index+1] = ans.created_at # line 2
end
end
if i have line 1 only in code not line 2 then this below code work fine for me
#answers = hash_answers.sort_by { |key, value| value }.reverse
but i also want to sort it by craeted_at
How i can achive this or make hash in another way
Any help will be most appreciated
Thanks
answers.sort_by do |ans|
[ans.net_votes, ans.created_at]
end
Then in your Answers class
def net_votes
votes_up_by_all_users - votes_down_by_all_users
end
You shouldn't have to pass an object to itself as a variable as in ans.votes_up_by_all_users(ans). Objects always know about themselves.
Usually you can sort on a number of things by creating an array of these things and use that as your sort key:
#answers = hash_answers.sort_by { |k, v| [ v[:created_at], v[:count] }
This is dependent on having a sortable structure to start with. You're jamming two entirely different things into the same hash. A better approach might be:
hash_answers[ans.id] = {
:id => ans.id,
:count => voted_up_users.count -voted_down_users.count,
:created_at => ans.created_at
}
You can adjust the order of the elements in the array to sort in the correct order.

Resources