I have a parent hash which changes and I want to ensure that the child hashes take these changes but also retain keys that they had before and those should not be lost
These are the sample hashes that I have
one = {"endpoints"=>["get_route"], "features"=>["channel_selection"], "get_route"=>{"output"=>["total_length", "seca_length"], "options"=>["panama", "abcd"]}}
other = {"endpoints"=>["get_route"], "features"=>["channel_selection"], "get_route"=>{"output"=>["total_length", "seca_length"], "options"=>["panama", "suez", "kiel"]}}
I want the other hash to now look like
other = {"endpoints"=>["get_route"], "features"=>["channel_selection"], "get_route"=>{"output"=>["total_length", "seca_length"], "options"=>["panama", "abcd", suez", "kiel"]}}
I have tried the following code but it is not working
result = propogate_changes(one, other)
def propogate_changes(one, other)
one_keys = one.keys
other_keys = other.keys
combined = Hash.new
unique_keys = one_keys.concat(other_keys).uniq
unique_keys.each do |key|
if(one[key].is_a?(Array)) then
# if(other[key] == nil) then
# combined[key] = one[key]
# else
combined[key] = one[key].concat(other[key]).uniq
# end
else
combined[key] = add_allowance(one[key], other[key])
end
end
return combined
end
The above code fails when a key is present in one but missing in another
I also tried merge, deep_merge, reverse_merge but they all overwrite my other hash with one hash but none of them retain the original data.
Any advise on this will be appreciated
Try this custom merge logic.
def find_missing_items_in_arr(arr1, arr2)
arr1_size = arr1.size
arr2_size = arr2.size
if (arr1_size == arr2_size) && (arr1 & arr2).size == arr1_size
return [] # Same array
end
arr2 - arr1
end
def custom_merge(target_hash, source_hash)
# If you want to preserve frozen state of entries, please use `clone`
duped_target_hash = target_hash.dup
source_hash.each do |k, v|
unless duped_target_hash.key?(k)
duped_target_hash[k] = v
next
end
case v
when Array
missing_items_in_arr = find_missing_items_in_arr(duped_target_hash[k], v)
if missing_items_in_arr.size > 0
duped_target_hash[k] += missing_items_in_arr
end
when Hash
duped_target_hash[k] = custom_merge(duped_target_hash[k], v)
else
# Nothing to do here
end
end
duped_target_hash
end
Usage
one = {
"endpoints"=>["get_route"],
"features"=>["channel_selection"],
"get_route"=> {
"output"=> ["total_length", "seca_length"],
"options"=> ["panama", "abcd"]
}
}
other = {
"endpoints"=>["get_route"],
"features"=>["channel_selection"],
"get_route"=> {
"output"=> ["total_length", "seca_length"],
"options"=> ["panama", "suez", "kiel"]
}
}
rs_hash = custom_merge(other, one)
puts rs_hash
Note: Rails provides a deep_merge but this can be used outside Rails. I have tested and it returns your desired output. Also it handles more nested entries like
one = {
"endpoints"=>["get_route"],
"features"=>["channel_selection"],
"get_route"=> {
"output"=> ["total_length", "seca_length"],
"options"=> ["panama", "abcd"],
"custom_options" => {
"custom_output" => ["abc"],
"custom_input" => ["xyz" ]
}
}
}
other = {
"endpoints"=>["get_route"],
"features"=>["channel_selection"],
"get_route"=> {
"output"=> ["total_length", "seca_length"],
"options"=> ["panama", "suez", "kiel"],
"custom_options" => {
"custom_output" => ["abc", "def"]
}
}
}
Hope this helps.
I have some data:
data = {
"total_records"=>3,
"records"=>
[{"title"=>"Val1",
"coins"=>1},
{"title"=>"Val2",
"coins"=>1},
{"title"=>"Val3",
"coins"=>1}]
}
How would I go about deleting the records that have title = 'Val1'||'Val2'?
I tried something along these lines:
#data.records.each_value do |e|
if exceptions.include?(e.title)
delete #envelopes.records.e
end
end
But I get a no method error on #data.records.
So simple
data['records'].delete_if{ |h| %w(Val1 Val2).include?(h['title']) }
You can also do this:
data['records'].delete_if{ |h| (h['title'] == 'Val1') || (h['title'] == 'Val2') }
I have this in models_name:
model_names = Object.constants.collect { |sym| Object.const_get(sym) }.select { |constant| constant.class == Class && constant.include?(Mongoid::Document) }.collect { |klass| klass.name }
the result is:
["Model_name1","Model_name2","Model_name3"]
I need something like:
[{"Model1":"Count"},{"Model2":"Count"},{"Model3":"Count"}]
I need show in a chart all models and object counter inside each model is working with morris.js
You can see the example:
http://jsbin.com/uzosiq/2/embed?javascript,live
Thank you very much!
that last one:
collect { |klass| klass.name }
rewrite it as
collect { |klass| [klass.name, klass.count] }
this should return an array of arrays of 2 elements (classname and count). If the variable to which this is assigned is called a, just do this:
Hash[a]
now you have a hash at your disposal. Now you can do this:
Hash[a].map do |k, v|
{k => v}
end
and now you have an array of hashes of only one key-value assoc each. Which I think it is what you want.
module Foo
end
class Bar
include Foo
end
1.upto(5) { Bar.new }
model_names = Object.constants.collect { |sym| Object.const_get(sym) }.
select { |constant| constant.class == Class && constant.include?(Foo) }.
inject({}) do |m,klass|
m.update klass.name => ObjectSpace.each_object(klass).to_a.size
end
p model_names
{"Bar"=>5}
Will work only on MRI based rubies, so no jRuby nor Runbinius.
Here is a live demo
I have an array of objects and I need to find the last element that matches a specific condition. I tried to do it with each_reverse, but it ended up with too much of a code:
matching_item = nil
items.reverse_each do |item|
if (item.type_id == 10)
matching_item = item
break
end
end
Is it possible to make it shorter?
Try:
matching_item = items.reverse.find{ |i| i.type_id == 10 }
I would probably use Array#select and return the last match:
matching_item = items.select {|i| i.type_id == 10}.last
Leave off the .last if you decide you want all matches:
matching_items = items.select {|i| i.type_id == 10}
items.reverse_each.detect{|item| iterm.type_id == 10}
#or
items[items.rindex{|item| item.type_id == 10}]
I am using the following logic to update a list item based on a criteria.
def update_orders_list(order)
#orders.delete_if{|o| o.id == order.id}
#orders << order
end
Ideally, I would have preferred these approaches:
array.find_and_replace(obj) { |o| conditon }
OR
idx = array.find_index_of { |o| condition }
array[idx] = obj
Is there a better way?
array.map { |o| if condition(o) then obj else o }
maybe?
As of 1.8.7, Array#index accepts a block. So your last example should work just fine with a minor tweak.
idx = array.index { |o| condition }
array[idx] = obj