Merging hashes with values having the same key getting appended - ruby

I wanted to know what would be the best way to achieve this:
my_hash_1 = { fruits: { red: 'apple' }, vegetables: { green: 'spinach' } }
my_hash_2 = { fruits: { green: 'grapes' } }
expected_output = { fruits: { red: 'apple', green: 'grapes' }, vegetables: { green: 'spinach' } }
I have looked into using merge! but that gives me with output:
{:fruits=>{:green=>"grapes"}, :vegetables=>{:green=>"spinach"}}

merge isn't recursive, so it only merges the fruits and vegetables items in the two hashes with its default behaviour. You need to provide a block to merge! to go down to the next level hash and merge them with the default merge too.
my_hash_1 = { fruits: { red: 'apple' }, vegetables: { green: 'spinach' } }
my_hash_2 = { fruits: { green: 'grapes' } }
expected_output = my_hash_1.merge(my_hash_2) { |_, h1, h2| h1.merge(h2) }
p expected_output
Result
{:fruits=>{:red=>"apple", :green=>"grapes"}, :vegetables=>{:green=>"spinach"}}

my_hash_1 = { fruits: { red: 'apple' }, vegetables: { green: 'spinach' } }
my_hash_2 = { fruits: { green: 'grapes' } }
my_hash_3 = { fruits: { yellow: 'mango' },spice: { red: 'chilli' }}
# extended method (multiple hash)
def merge_hash(arr_of_hash)
result = {}
arr_of_hash.each do |_hash|
_hash.each{ |k,v| result[k].nil? ? result[k] = v : result[k].merge!(v) }
end
result
end
puts merge_hash([my_hash_1,my_hash_2,my_hash_3])
# {:fruits=>{:red=>"apple", :green=>"grapes", :yellow=>"mango"}, :vegetables=>{:green=>"spinach"}, :spice=>{:red=>"chilli"}}

The two answers do solve what I was trying to do but since I was working with only two hashes and knew what keys to merge, I ended up doing something like this which I found was a simpler approach.
my_hash_1 = { fruits: { red: 'apple' }, vegetables: { green: 'spinach' } }
my_hash_2 = { fruits: { green: 'grapes' } }
my_hash_1[:fruits]&.merge!(my_hash_2[:fruits]) unless my_hash_2[:fruits].nil?
which would essentially merge the two in place in my_hash_1.

Related

Is there a more efficient way to refactor the iteration of the hash on ruby?

I have a iteration here:
container = []
summary_data.each do |_index, data|
container << data
end
The structure of the summary_data is listed below:
summary_data = {
"1" => { orders: { fees: '25.00' } },
"3" => { orders: { fees: '30.00' } },
"6" => { orders: { fees: '45.00' } }
}
I want to remove the numeric key, e.g., "1", "3".
And I expect to get the following container:
[
{
"orders": {
"fees": "25.00"
}
},
{
"orders": {
"fees": "30.00"
}
},
{
"orders": {
"fees": "45.00"
}
}
]
Is there a more efficient way to refactor the code above?
Appreciate for any help.
You can use Hash#values method, like this:
container = summary_data.values
If the inner hashes all have the same structure, the only interesting information are the fees:
summary_data.values.map{|h| h[:orders][:fees] }
# => ["25.00", "30.00", "45.00"]
If you want to do some calculations with those fees, you could convert them to numbers:
summary_data.values.map{|h| h[:orders][:fees].to_f }
# => [25.0, 30.0, 45.0]
It might be even better to work with cents as integers to avoid any floating point error:
summary_data.values.map{|h| (h[:orders][:fees].to_f * 100).round }
=> [2500, 3000, 4500]
You need an array having values of provided hash. You can get by values method directly.
summary_data.values

Update shape color using Ruby Google Slides API

I'm trying to update an ellipsis shape using the google slides api in ruby. This is the code:
shape_properties = {
shape_background_fill: {
solid_fill: {
color: {
rgb_color: {
red: 1.0,
green: 0,
blue: 0
}
}
}
}
}
requests = [{
update_shape_properties: {
object_id: ellipse.object_id,
fields: 'shapeBackgroundFill',
shape_properties: shape_properties,
},
}]
# Execute the request.
req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests: requests)
response = #slides.batch_update_presentation(presentation_id,req)
Another code that I've tried with the same error is this one:
rgb_color = Google::Apis::SlidesV1::RgbColor.new(red: 1.0, green: 0, blue: 0)
color = Google::Apis::SlidesV1::OpaqueColor.new(rgb_color: rgb_color)
solid_fill = Google::Apis::SlidesV1::SolidFill.new(color: color)
shape_background_fill = Google::Apis::SlidesV1::ShapeBackgroundFill.new(solid_fill: solid_fill)
shape_properties = Google::Apis::SlidesV1::ShapeProperties.new(shape_background_fill: shape_background_fill)
requests = [{
update_shape_properties: {
object_id: ellipse.object_id,
fields: 'shapeBackgroundFill',
shape_properties: shape_properties,
},
}]
req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests: requests)
response = #slides.batch_update_presentation(presentation_id, req)
I get this error:
`check_status': badRequest: Invalid requests[0].updateShapeProperties: The object () could not be found. (Google::Apis::ClientError)
Any idea why is it fails?
try using object_id_prop instead of object_id in update_shape_properties.
shape_properties = {
shape_background_fill: {
solid_fill: {
color: {
rgb_color: {
red: 1.0,
green: 0,
blue: 0
}
}
}
}
}
requests = [{
update_shape_properties: {
object_id_prop: ellipse.object_id,
fields: 'shapeBackgroundFill',
shape_properties: shape_properties,
},
}]
# Execute the request.
req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests:
requests)
response = #slides.batch_update_presentation(pres`entation_id,req)
Because UpdateShapePropertiesRequest has object_id_prop accessor instead of object_id.
That's why object_id name is already used in Object.

How do I assign part of a hash to another variable?

Trying to assign part of a has to another variable. I have a hash. Something like:
hash = {
"cupcake" => {
"a" => 1
},
"muffin" => {
"b" => 2
}
}
When I do something like:
cupcake = hash["cupcake"]
cupcake is nil after this code.
If you want string keys you have to use this syntax
hash = {
"cupcake" => {
"a" => 1
},
"muffin" => {
"b" => 2
}
}
Syntax with colons is for symbol keys
hash = {
cupcake: {
a: 1
},
muffin: {
b: 2
}
}
cupcake = hash[:cupcake]

leaflet geoJSON filtering

I am using L.GeoJSON.AJAX to load my json. I have the following to style the markers:
var weakClusterMarkerOptions = {
radius: 5,
fillColor: "#FFFF00",
color: "#000",
weight: 2,
opacity: 1,
fillOpacity: 0.8
},
strongClusterMarkerOptions = {
radius: 7,
fillColor: "#CC0000",
color: "#CC0000",
opacity: 1,
fillOpacity: 0.8
};
function customizeClusterIcon(feature, latlng) {
if (feature.properties.strongCl === 'strong') {
return L.circleMarker(latlng, strongClusterMarkerOptions);
} else {
return L.circleMarker(latlng, weakClusterMarkerOptions);
}
}
I then use the following to filter:
function toggleStrength(strength, showLayer) {
jsonLayer.refilter(function (feature, layer) {
if (strength == 'all') {
return true;
} else {
if (showLayer) {
return feature.properties.strongCl === strength;
}
}
});
}
The issue here is that when I filter the border of the circleMarker disappears but the marker is still visible, specifically the fill colour.
So I ended up creating LayerGroups for each condition (weak and strong) then clearing and adding the respective groups depending on the filter.
toggleStrength = function (layer, showLayer, theMap) {
switch (layer) {
case 'weak':
if (showLayer) {
theMap.addLayer(weakGroup);
} else {
theMap.removeLayer(weakGroup);
}
break;
case 'strong':
if (showLayer) {
theMap.addLayer(strongGroup);
} else {
theMap.removeLayer(strongGroup);
}
break;
default:
clearAll(theMap);
if (showLayer) {
theMap.addLayer(weakGroup);
theMap.addLayer(strongGroup);
}
break;
}
};
Your fillColor and color has same value #CC0000. Probably border is there. It's just has same color as fill.

Python: Searching a multi-dimensional dictionary for a key

I'm using Python's JSON decoding library with Google Maps API. I am trying to obtain the zip code of an address but it sometimes resides in different dictionary key. Here are two examples (I've trimmed the JSON to what is relevant):
placemark1 = {
"AddressDetails": {
"Country": {
"AdministrativeArea": {
"SubAdministrativeArea": {
"Locality": {
"PostalCode": {
"PostalCodeNumber": "94043"
}
}
}
}
}
}
}
(View full JSON)
placemark2 = {
"AddressDetails": {
"Country" : {
"AdministrativeArea" : {
"Locality" : {
"PostalCode" : {
"PostalCodeNumber" : "11201"
}
}
}
}
}
}
(View full JSON)
So the zipcodes:
zipcode1 = placemark1['AddressDetails']['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['PostalCode']['PostalCodeNumber']
zipcode2 = placemark2['AddressDetails']['Country']['AdministrativeArea']['Locality']['PostalCode']['PostalCodeNumber']
Now I was thinking perhaps I should just search the multi-dimensional dictionary for "PostalCodeNumber" key. Does anyone have any idea on how to accomplish this? I want it to look something like this:
>>> just_being_a_dict = {}
>>> just_a_list = []
>>> counter_dict = {'Name': 'I like messing things up'}
>>> get_key('PostalCodeNumber', placemark1)
"94043"
>>> get_key('PostalCodeNumber', placemark2)
"11201"
>>> for x in (just_being_a_dict, just_a_list, counter_dict):
... get_key('PostalCodeNumber', x) is None
True
True
True
def get_key(key,dct):
if key in dct:
return dct[key]
for k in dct:
try:
return get_key(key,dct[k])
except (TypeError,ValueError):
pass
else:
raise ValueError
placemark1 = {
"AddressDetails": {
"Country": {
"AdministrativeArea": {
"SubAdministrativeArea": {
"Locality": {
"PostalCode": {
"PostalCodeNumber": "94043"
}
}
}
}
}
}
}
placemark2 = {
"AddressDetails": {
"Country" : {
"AdministrativeArea" : {
"Locality" : {
"PostalCode" : {
"PostalCodeNumber" : "11201"
}
}
}
}
}
}
just_being_a_dict = {}
just_a_list = []
counter_dict = {'Name': 'I like messing things up'}
for x in (placemark1, placemark2, just_being_a_dict, just_a_list, counter_dict):
try:
print(get_key('PostalCodeNumber', x))
except ValueError:
print(None)
yields
94043
11201
None
None
None
from collections import Mapping
zipcode1 = {'placemark1':{'AddressDetails':{'Country':{'AdministrativeArea':{'SubAdministrativeArea':{'Locality':{'PostalCode':{'PostalCodeNumber':"94043"}}}}}}}}
zipcode2 = {'placemark2':{'AddressDetails':{'Country':{'AdministrativeArea':{'Locality':{'PostalCode':{'PostalCodeNumber':'11201'}}}}}}}
def treeGet(d, name):
if isinstance(d, Mapping):
if name in d:
yield d[name]
for it in d.values():
for found in treeGet(it, name):
yield found
yields all matching values in tree:
>>> list(treeGet(zipcode1, 'PostalCodeNumber'))
['94043']
>>> list(treeGet(zipcode2, 'PostalCodeNumber'))
['11201']

Resources