Accessing array within a hash within a hash (adding new values) - ruby

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.

Related

Using delete_if to delete single value of hash not the entire hash RUBY

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.

Adapter pattern for Gem that uses multiple composed classes?

I'm using the Axlsx gem (https://github.com/randym/axlsx) for generating excel files. I want to refactor my code so that I have a wrapper around the gem just in case I want to switch gems.
Going through this reminds me of the Adapter design pattern. However, there are just so many objects nested underneath the main Package object that I am getting confused as to how to actual create an adapter for it. Ex:
You create a Package object
You access the Workbook within this Package objet
You access the Sheet from the Workbook object, and vice versa
Here are some of my classes:
class ReportGenerator::Base
...
def create_workbook
...
#package = Axlsx::Package.new <---------------------------
#workbook = #package.workbook <---------------------------
#workbook.use_shared_strings = true
end
class Sheet::Base
def initialize(workbook, question, options = {})
...
#sheet = workbook.add_worksheet(:name => sheet_name) <---------------------------
end
def styles
#styles ||= {
"title" => #sheet.workbook.styles.add_style(:sz => 20, :b => true, :alignment => { :wrap_text => true }),
"bold" => #sheet.workbook.styles.add_style(:b => true),
"header" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}, :border => {:style => :thin, :color => "FFFFFF"}),
"subheader" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}),
"subheader_border_left" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}, :border => {:style => :thin, :color => "FFFFFF", :edges => [:left]}),
"blue_link" => #sheet.workbook.styles.add_style(:fg_color => '0000FF'),
"wrap_text" => #sheet.workbook.styles.add_style(:alignment => { :wrap_text => true, :horizontal => :left }),
"percentage" => #sheet.workbook.styles.add_style(:format_code => "0.00%")
}
end
Here's my first stab:
class ExcelWriter
def initialize
#package = Axlsx::Package.new
end
def workbook
#package.workbook
end
# starting to feel like it's not doable within one class..?
end
There are so many classes involved that it feels like I can't wrap everything into one adapter? Or maybe I'm doing it wrong? Any tips would be welcomed.
Focus on what you're actually using and not on existing infrastracture of Axlsx gem. This way you may combine work of several Axlsx objects into 1 method call.
I don't know what you're actually using, so it's hard for me to say which objects do you need.

Ruby comparing values in hashes and returning the same structure

I have 2 hashes like so:
stored_hash = {
:name => "hash_1",
:version => "1.0",
:values => {
:value_1 => "some_value",
:value_2 => "some_other_value",
:value_3 => "a_new_value",
...
:value_x => "some_x_value"
}
}
compare_hash = {
:name => "hash_2",
:version => "2.0",
:values => {
:different_1 => "some_value",
:different_2 => "some_other_value",
:different_3 => "a_new_value",
...
:different_x => "some_x_value"
}
}
I find the common values in the :values key of both the hashes like this :
same_values = hash[:values].values & compared_hash[:values].values
And once I get it, I want to return a new hash similar to 'stored_hash', but with the :values of it containing the same_values I found before.
For example, if both hashes have "some_value", "some_other_value", "a_new_value", my new hash should look like :
new_hash = {
:name => "hash_1",
:version => "1.0",
:values => {
:value_1 => "some_value",
:value_2 => "some_other_value",
:value_3 => "a_new_value"
}
}
This should work for you :
stored_hash = {
:name => "hash_1",
:version => "1.0",
:values => {
:value_1 => "some_value",
:value_2 => "some_other_value",
:value_3 => "a_new_value",
:value_x => "some_a_value"
}
}
compare_hash = {
:name => "hash_2",
:version => "2.0",
:values => {
:different_1 => "some_value",
:different_2 => "some_other_value",
:different_3 => "a_new_value",
:different_x => "some_b_value"
}
}
common_values = compare_hash[:values].values & stored_hash[:values].values
new_hash = stored_hash.dup
new_hash[:values] = {}
common_values.each_with_index do |value, index |
new_hash[:values]["value_#{index+1}".to_sym] = value
end
new_hash
# => new_hash = {
# :name => "hash_1",
# :version => "1.0",
# :values => {
# :value_1 => "some_value",
# :value_2 => "some_other_value",
# :value_3 => "a_new_value"
# }
# }
This is one way you could obtain your desired result.
Code
require 'set'
def make_new_hash(stored_hash, compare_hash)
new_hash = stored_hash.dup
compare_values =
(stored_hash[:values].values & compare_hash[:values].values).to_set
values_hash = new_hash[:values]
keys_to_keep = values_hash.keys.select { |k|
compare_values.include?(values_hash[k])}
new_hash[:values] =
Hash[keys_to_keep.zip(values_hash.values_at(*keys_to_keep))]
new_hash
end
Example
stored_hash = {
:name => "hash_1",
:version => "1.0",
:values => {
:value_1 => "some_value",
:value_2 => "some_other_value",
:value_3 => "a_new_value",
:value_x => "some_x_value"
}
}
compare_hash = {
:name => "hash_2",
:version => "2.0",
:values => {
:different_1 => "some_value",
:different_2 => "a_new_value",
:different_3 => "some_strange_value",
:different_x => "some_x_value"
}
}
Note that I've made a small change in stored_hash from that given in the question.
make_new_hash(stored_hash, compare_hash)
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"some_value", :value_3=>"a_new_value",
# :value_x=>"some_x_value"}}
Explanation
Create a copy of stored_hash:
new_hash = stored_hash.dup
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"some_value",
# :value_2=>"some_other_value",
# :value_3=>"a_new_value",
# :value_x=>"some_x_value"}}
We only want to keep a key k of new_hash[:values] if new_hash[:values][k] is one of the values in both the hash stored_hash[:values] and the hash compare_hash[:values], so we obtain those values:
compare_values =
(stored_hash[:values].values & compare_hash[:values].values).to_set
# => #<Set: {"some_value", "a_new_value", "some_x_value"}>
I chose to save them in a set both to speed lookup and to obtain unique values. They could alternatively be saved to array like so:
(stored_hash[:values].values & compare_hash[:values].values).uniq
#=> ["some_value", "a_new_value", "some_x_value"]
The code that follows is the same if an array is used rather than a set.
To simpify, let's create a variable:
values_hash = new_hash[:values]
#=> {:value_1=>"some_value", :value_2=>"some_other_value",
# :value_3=>"a_new_value", :value_x=>"some_x_value"}
Next determine the keys of New_hash[:values] we wish to keep:
keys_to_keep = values_hash.keys.select { |k|
compare_values.include?(values_hash[k])}
#=> [:value_1, :value_3, :value_x]
All keys other than :value_2 are kept. That key is not kept because comparative_hash[:values] does not have a value "some_other_value".
We can now construct the updated value of new_hash[:values]:
new_hash[:values] =
Hash[keys_to_keep.zip(values_hash.values_at(*keys_to_keep))]
#=> {:value_1=>"some_value", :value_3=>"a_new_value",
# :value_x=>"some_x_value"}
Lastly, we return new_hash:
new_hash
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"some_value", :value_3=>"a_new_value",
# :value_x=>"some_x_value"}}
Let's confirm that stored_hash will not be changed when a value of new_hash[:values] is changed:
new_hash[:values][:value_1] = 'cat'
new_hash
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"cat", :value_3=>"a_new_value",
# :value_x=>"some_x_value"}}
stored_hash
#=> {:name=>"hash_1", :version=>"1.0",
# :values=>{:value_1=>"some_value", :value_2=>"some_other_value",
# :value_3=>"a_new_value", :value_x=>"some_x_value"}}

Using splat arguments to fetch from multidimensional hash

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"

Complex map by objects property in ruby

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

Resources