I am unable to come up with a nice way to access a multidimensional Hash with supplied key names in a splat operator - any suggestions?
Example:
I am having a Hash like
{
'key' => 'value',
'some' => {
'other' => {
'key' => 'othervalue'
}
}
}
and a function definition def foo(*args)
I want to return foo('key') value and foo('some','other','key') othervalue. All I can come up with are rather long and ugly for loops with a lot of nil? checks, and am somehow sure I am missing a more ruby-ish way to do this nice and short. Any hints are appreciated.
Update
Using the reply by Patrick below, I came up with
def foo(hash, *args)
keys.reduce(hash, :fetch)
end
which works as I expect it to. Thanks!
In some other languages this is known as get_in, for example in Clojure and Elixir. Here's a functional-ish implementation in Ruby:
class Hash
def get_in(*keys)
keys.reduce(self, :fetch)
end
end
Usage:
h = {
'key' => 'value',
'some' => {
'other' => {
'key' => 'othervalue'
}
}
}
h.get_in 'some'
#=> {
# "other" => {
# "key" => "othervalue"
# }
# }
h.get_in 'some', 'other'
#=> {
# "key" => "othervalue"
# }
h.get_in 'some', 'other', 'key'
#=> "othervalue"
Related
I have a array
array_hash = [
{
"array_value" => 1,
"other_values" => "whatever",
"inner_value" => [
{"iwantthis" => "forFirst"},
{"iwantthis2" => "forFirst2"},
{"iwantthis3" => "forFirst3"}
]
},
{
"array_value" => 2,
"other_values" => "whatever2",
"inner_value" => [
{"iwantthis" => "forSecond"},
{"iwantthis2" => "forSecond2"},
{"iwantthis3" => "forSecond3"}
]
},
]
I want to delete inner value or pop it out (i prefer pop).
So my output should be this:
array_hash = [
{
"array_value" => 1,
"other_values" => "whatever"
},
{
"array_value" => 2,
"other_values" => "whatever2"
},
]
I tried delete_if
array_hash.delete_if{|a| a['inner_value'] }
But it deletes all data in the array. Is there any solution?
try this:
array_hash.map{ |a| {'array_value' => a['array_value'], 'other_values' => a['other_values'] }}
You are telling ruby to delete all hashes that have a key called inner_value. That explains why the array remains empty.
What you should probably do instead is:
array_hash.each { |x| x.delete 'inner_value' }
which means: for each hash in this array, erase the inner_value key.
Well I found it,
array_hash_popped = array_hash.map{ |a| a.delete('inner_value') }
This will pop (because I want pop as stated in the question) the inner_value out and hence inner value will be reduced/deleted from array_hash.
I have a huge hash (JSON) that I want to compare to a "master key" by deleting the values that are dissimilar then totaling a value set.
I thought it would be a good way to handle test scoring with complex scoring criterion.
Advice about how to do this? Any gems exist to make my life easier?
{
"A" => 10,
"B" => 7,
etc
....
The hash is constructed like test[answer] => test[point_value] and the question key/value is the answer/point value.
So if I want to compare to a master_key and remove dissimilar items (not remove similar ones like arr1-arr2 does...then total the values, what would be best?
After converting the hashes to ruby hashes i'd do something like this
tester = { :"first" => { :"0" => { :"0" => { :"B" => 10 }, :"1" => { :"B" => 7 }, :"2" => { :"B" => 5 } } }}
master = { :"first" => { :"0" => { :"0" => { :"A" => 10 }, :"1" => { :"B" => 7 }, :"2" => { :"B" => 5 } } }}
tester.reduce(0) do |score, (test, section)|
section.each do |group, questions|
questions.each do |question, answer|
if answer.keys.first == master[test][group][question].keys.first
score += answer.values.first
end
end
end
score
end
I'm trying to create different kind of structure from the array I currently have in my code.
I've got an array of objects (active directory objects) so lets say i got a from db :
a = [o1,o2,o3,o4,o5]
My object has property source_id and name which are the relevant properties.
I want to create structure like this (I want hash) from the data I have in my array :
objects = Hash.new { |hash, key| hash[key] = [] }
And this would be one example of how to put the data inside new structure:
a.each do |ob|
objects[ob.source_id] << {
:new => '',
:name => {:unformated => ob.name, :formatted => ob.format(:name)}
}
end
I'm trying to replicate the same structure and it's not working out in my case :
a.group_by(&:source_id).map do |k,v|
{
k=> {
{
:new => '',
:name => {:unformated => v.name, :formatted => ob.format(:name)}
}
}
}
end.reduce(:merge)
This is the error I get :
! #<NoMethodError: undefined method `name' for #<Array:0xae542b4>>
With group_by your values in the a.group_by(&:source_id).map are going to be arrays of elements sharing the same source_id and not individual elements.
The following code may do what you wish:
a.group_by(&:source_id).map do |k,v|
{
k => v.map do |e|
{
:new => '',
:name => {:unformated => e.name, :formatted => e.format(:name)}
}
end
}
end.reduce(:merge)
Heres a one liner to do the same
new_hash = a.each_with_object({}) { |o, hash| hash[o.source_id] = {:new => '', :unformatted => o.name, :formatted => o.format(:name)} }
This does require ruby 1.9.3 though
Thank you for the help in advance!
I'd like to add or push a value into my array which is currently within a person hash and task hash.
Look below at the add task section task.merge!
task = {
"person1" => {
:high => ["email", "phone","email2","talk","meeting"],
:mid => ["task1","task2"],
:low => ["task1","task2"] },
"person2" => {
:high => ["email", "phone","email2","talk","meeting"],
:mid => ["task1","task2"],
:low => ["task1","task2"] },
"person3" => {
:high => ["email", "phone","email2","talk","meeting"],
:mid => ["task1","task2"],
:low => ["task1","task2"] },
}
puts task["person1"][:high][0]
# create task
#create new person hash within task hash
#already have :high, :mid, & :low hash
# add task
#add a task to the array within either high,mid, or low
task.merge!("person1" => {:high => "#{new_task}")
# remove task
#delete method
task.merge! "person1" => {:high => "#{new_task}"}
You simply had an error there. I removed the parenthesis to make it maybe a little more obvious.
This question already has answers here:
How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?
(16 answers)
Closed 7 years ago.
Given a hash such as:
AppConfig = {
'service' => {
'key' => 'abcdefg',
'secret' => 'secret_abcdefg'
},
'other' => {
'service' => {
'key' => 'cred_abcdefg',
'secret' => 'cred_secret_abcdefg'
}
}
}
I need a function to return service/key in some cases and other/service/key in other cases. A straightforward way is to pass in the hash and an array of keys, like so:
def val_for(hash, array_of_key_names)
h = hash
array_of_key_names.each { |k| h = h[k] }
h
end
So that this call results in 'cred_secret_abcdefg':
val_for(AppConfig, %w[other service secret])
It seems like there should be a better way than what I've written in val_for().
def val_for(hash, keys)
keys.reduce(hash) { |h, key| h[key] }
end
This will raise an exception if some intermediate key is not found. Note also that this is completely equivalent to keys.reduce(hash, :[]), but this may very well confuse some readers, I'd use the block.
%w[other service secret].inject(AppConfig, &:fetch)
appConfig = {
'service' => {
'key' => 'abcdefg',
'secret' => 'secret_abcdefg'
},
'other' => {
'service' => {
'key' => 'cred_abcdefg',
'secret' => 'cred_secret_abcdefg'
}
}
}
def val_for(hash, array_of_key_names)
eval "hash#{array_of_key_names.map {|key| "[\"#{key}\"]"}.join}"
end
val_for(appConfig, %w[other service secret]) # => "cred_secret_abcdefg"
Ruby 2.3.0 introduced a new method called dig on both Hash and Array that solves this problem entirely.
AppConfig.dig('other', 'service', 'secret')
It returns nil if the key is missing at any level.