Advanced grouping in Ruby - ruby-on-rails

I want to group events by their days.
Prefact:
Available days are 1,2,3,4,5,6,7
One event can not contain duplicates. e.g [1,1,2,3]
Setup is Ruby 1.9.2 with Rails 3.2
An event is containing like 8-10 more attributes(unnecessary to include in example, but should be considered, these attributes should still be there after grouping.) In other words the event objects should not be altered only grouped as is.
Consider an array with objects:
events = [
{
:name => "event1",
:days => [1,2,3,4,5]
},
{
:name => "event2",
:days => [1,4,5]
},
{
:name => "event3",
:days => [1]
},
{
:name => "event4",
:days => [2]
},
{
:name => "event5",
:days => [3]
},
{
:name => "event6",
:days => [4]
},
{
:name => "event7",
:days => [5]
},
{
:name => "event8",
:days => [1,2,3]
},
{
:name => "event9",
:days => [1,5]
},
{
:name => "event10",
:days => [1,2]
},
{
:name => "event11",
:days => [1,2,3,4,5]
}
]
To be grouped an event must have at least 3 days. And these days should be in numerically order.
Example(should be grouped): [1,2,3]
Example(should not be grouped): [1,4,5]
Events that's not suitable for grouping should be placed in each day they contain.
Example: [1,4,5] should be placed in 1,4 and 5.
The desired result of events array above:
[
{
:heading => "1",
:events => [
{
:name => "event3",
:days => [1]
},
{
:name => "event9",
:days => [1,5]
},
{
:name => "event10",
:days => [1,2]
},
{
:name => "event2",
:days => [1,4,5]
}
]
},
{
:heading => "2",
:events => [
{
:name => "event4",
:days => [2]
},
{
:name => "event10",
:days => [1,2]
}
]
},
{
:heading => "3",
:events => [
{
:name => "event5",
:days => [3]
}
]
},
{
:heading => "4",
:events => [
{
:name => "event6",
:days => [4]
},
{
:name => "event2",
:days => [1,4,5]
}
]
},
{
:heading => "5",
:events => [
{
:name => "event7",
:days => [5]
},
{
:name => "event9",
:days => [1,5]
},
{
:name => "event2",
:days => [1,4,5]
}
]
},
{
:heading => "1-3",
:events => [
{
:name => "event8"
}
]
},
{
:heading => "1.5",
:events => [
{
:name => "event1"
},
{
:name => "event11"
}
]
}
]
It is very advanced Ruby here. Maybe too advanced for me, everything I've tried ends up missing one part of the equation. But hey it's Ruby, it shouldn't be that hard?
EDIT: Updated example with clarification and corrected expected output

require 'pp'
pp(events.inject(Hash.new { |h, k| h[k] = [] }) do |m, e|
days = e[:days]
event = { :name => e[:name] }
if days.size >= 3 && days.last - days.first + 1 == days.size
m["%d-%d" % [days.first, days.last]] << event
else
days.each { |d| m[d.to_s] << event }
end
m
end)

I would modify DigitalRoss's answer slightly. I would change
if days.size >= 3 && days.last - days.first + 1 == days.size
to
if days.size >= 3 && (days.first..days.last).to_a == days
This will catch [1,1,3,4]...
It would also be wise to sort the days array before testing!

Related

ARRAY ACCESS AND INSERTION RECORD DATABASE

I would need to access the array to retrieve the information and insert it into the database (Code, Customer, Phone1, Phone2). Someone can help me?
{
:recordset => {
:row => [
[0] {
:property => [
[0] {
:name => "Code",
:value => "C0001"
},
[1] {
:name => "Customer",
:value => "ROSSI MARIO"
},
[2] {
:name => "Phone1",
:value => "1234567890"
}
]
},
[1] {
:property => [
[0] {
:name => "Code",
:value => "C0002"
},
[1] {
:name => "Customer",
:value => "VERDE VINCENT"
},
[2] {
:name => "Phone1",
:value => "9876543210"
},
[3] {
:name => "Phone2",
:value => "2468101214"
}
]
}
]
},
:#xmlns => "http://localhost/test"
}
p.s. The Phone2 value during the SOAP call is only displayed if it is present in an archive
Thank you
Your data doesn't look like a valid ruby structure. Once you are able to convert it to be more ruby-like, you can make use of each_with_object to generate a well formatted set of attributes and their values from your data:
data
=> {:recordset=>{
:row=>[{
:property=>[
{:name=>"Code", :value=>"C0001"},
{:name=>"Customer", :value=>"ROSSI MARIO"},
{:name=>"Phone1", :value=>"1234567890"}
]
}, {
:property=>[
{:name=>"Code", :value=>"C0002"},
{:name=>"Customer", :value=>"VERDE VINCENT"},
{:name=>"Phone1", :value=>"9876543210"},
{:name=>"Phone2", :value=>"2468101214"}
]
}]
},
:#xmlns=>"http://localhost/test"}
data.keys
=> [:recordset, :#xmlns]
data[:recordset][:row].count
=> 2 # There are 2 set of attribute-value pairs
result = data[:recordset][:row].each_with_object([]) do |hash, out|
out << hash[:property].each_with_object({}) do |h, o|
o[h[:name]] = h[:value]
end
end
=> [{"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"},
{"Code"=>"C0002", "Customer"=>"VERDE VINCENT", "Phone1"=>"9876543210", "Phone2"=>"2468101214"}]
Now, you can iterate over result and create respective records in database.

Rails: Values not getting assigned to array when in a do loop

I have an array called components
components = [
{
:name => "Component 1",
:order => "1"
},
{
:name => "Component 2",
:order => "2"
},
{
:name => "Component 3",
:order => "3"
},
{
:name => "Component 4",
:order => "4"
}
]
And another array called subcomponent
subcomponents = [
{
:name => "Subcomponent 1A",
:order => "1A"
},
{
:name => "Subomponent 1B",
:order => "1B"
},
{
:name => "Component 2A",
:order => "2A"
},
{
:name => "Component 4A",
:order => "4A"
}
]
I'm trying to get it so that the subcomponents appear underneath a component object if they are a subcomponent of that object. This is the expected output:
components = [
{
:name => "Component 1",
:order => "1",
:subcomponents => [
{
:name => "Subcomponent 1A",
:order => "1A"
},
{
:name => "Subomponent 1B",
:order => "1B"
}
]
},
{
:name => "Component 2",
:order => "2",
:subcomponents => [
{
:name => "Component 2A",
:order => "2A"
}
]
},
{
:name => "Component 3",
:order => "3",
:subcomponent => []
},
{
:name => "Component 4",
:order => "4",
:subcomponent => [
{
:name => "Component 4A",
:order => "4A"
}
]
}
]
I've created a loop to try to do this:
components.each do |c|
c.class.module_eval { attr_accessor :subcomponents}
c.subcomponents = []
subcomponents.each do |s|
if /#{c["order"]}[A-Z]/ =~ s["order"]
#This is never assigned but it does make it into this statement
c.subcomponents << s
end
end
puts c.subcomponents.to_s # This prints []
end
As I've put in my code comments, the subcomponent is never assigned back to the component.subcomponents arrays even though the if statement is stepped into.
Am I missing something where the scope of component.subcomponents isn't accessible anymore or is at a different scope?
What should I be doing to ensure I can assign value component.subcomponents?
components.each do |c|
c[:subcomponents] = []
subcomponents.each do |s|
c[:subcomponents] << s if /#{c[:order]}[A-Z]/ =~ s[:order]
end
end
Hope this work.
components.each do |component|
component[:subcomponents] = []
end
subcomponents.each do |subcomponent|
order = subcomponent[:order][/\d+/]
components.each do |component|
if order == component[:order]
component[:subcomponents] << subcomponent
end
end
end
You can do it in O(n) using this:
subcomponent_hash = Hash.new { |h, k| h[k] = [] }
subcomponents.each do |sc|
order = sc[:order][/\d+/]
subcomponent_hash[order] << sc
end
components.each do |c|
c[:subcomponents] = subcomponent_hash[c[:order]]
end

Ruby on Rails How to build a Payment model to deal with Paypal REST SDK

I'd like to create a Payment model along the official Paypal Example on Github.
But I'm stuck in the creating of the model with the desired fields.
Payment.new({
:intent => "sale",
:payer => {
:payment_method => "paypal" },
:redirect_urls => {
:return_url => "http://localhost:3000/payment/execute",
:cancel_url => "http://localhost:3000/" },
:transactions => [{
:item_list => {
:items => [{
:name => "item",
:sku => "item",
:price => "5",
:currency => "USD",
:quantity => 1 }]},
:amount => {
:total => "5",
:currency => "USD" },
:description => "This is the payment transaction description." }]})
Starting with rails g model Payment intent:string ... I don't know how to create the nested fields like
:redirect_urls => {
:return_url => "http://localhost:3000/payment/execute",
:cancel_url => "http://localhost:3000/" }
and more deeper
:transactions => [{
:item_list => {
:items => [{
:name => "item",
:sku => "item",
:price => "5",
:currency => "USD",
:quantity => 1 }]},
Thanks for any help!
You can use OpenStruct to do this for you. It will be something like this :
paypal_hash = {
:intent => "sale",
:payer => {
:payment_method => "paypal" },
:redirect_urls => {
:return_url => "http://localhost:3000/payment/execute",
:cancel_url => "http://localhost:3000/" },
:transactions => [{
:item_list => {
:items => [{
:name => "item",
:sku => "item",
:price => "5",
:currency => "USD",
:quantity => 1 }]},
:amount => {
:total => "5",
:currency => "USD" },
:description => "This is the payment transaction description." }]}
paypal_obj = OpenStruct.new(paypal_hash)
paypal_obj.intent
# => "sales"

Rails how to group by nested hash

I have no clue to solve this question. I got a nested hash inside an array called data. Here is its structure.
data =
[
{
:id => 1,
:name => "S1",
:children => [
{
:id => 10,
:name => "S10",
:children => [
{
:id => 20,
:name => "S20"
}
]
}
]
},
{
:id => 1,
:name => "S1",
:children => [
{
:id => 10,
:name => "S10",
:children => [
{
:id => 21,
:name => "S21"
}
]
}
]
},
{
:id => 1,
:name => "S1",
:children => [
{
:id => 11,
:name => "S11",
:children => [
{
:id => 22,
:name => "S22"
}
]
}
]
}
]
As you can see, there are bunch of elements which have the same id in the first layer or second layer, so I need to group them.
I hope the result will be
result=
[
{
:id => 1,
:name => "S1",
:children => [
{
:id => 10,
:name => "S10",
:children => [
{
:id => 20,
:name => "S20"
},
{
:id => 21,
:name => "S21"
}
]
},
{
:id => 11,
:name => "S11",
:children => [
{
:id => 22,
:name => "S22"
}
]
}
]
}
]
I've tried somthing like
data.group_by{|s| s[:id]}
However, it would only group the first layer, I don't know how to group nested structure.
Yeah you need some kind of recursive method to recursively combine and group the nested children.
This produces the result you want:
def group(data)
r = {}
# Combine the children
data.each do |d|
if r[d[:id]]
r[d[:id]][:children] += d[:children]
else
r[d[:id]] = d
end
end
# Now group the children
r.values.map do |d|
d[:children] = group(d[:children]) if d[:children]
d
end
end

What is causing Aws::CloudFront::Errors::MalformedInput: Unexpected list element termination

I'm attempting to create a new AWS Cloudfront Distribution with v2 of the ruby AWS SDK and cannot figure out what is causing this error.
Aws::CloudFront::Errors::MalformedInput: Unexpected list element termination
client = Aws::CloudFront::Client.new
resp = client.create_distribution({
distribution_config: {
caller_reference: Time.now.to_i.to_s,
:aliases => {
:quantity => 1,
:items => [Name.generate_name]
},
:origins => {
:quantity => 1,
:items => [
{
:id => "#{self.id}-distribution",
:domain_name => "example-static.s3-website-us-east-1.amazonaws.com",
:origin_path => "/#{self.id}",
:custom_headers => {
:quantity => 0,
:items => []
},
:custom_origin_config => {
:http_port => 80,
:https_port => 443,
:origin_protocol_policy => "http-only",
:origin_ssl_protocols => {
:quantity => 3,
:items => ["TLSv1","TLSv1.1","TLSv1.2"]
}
}
}
]
},
:default_cache_behavior => {
:target_origin_id => "Custom-example-static.s3-website-us-east-1.amazonaws.com/#{self.id}",
:forwarded_values => {
:query_string => true,
:cookies => {
:forward => "none"
},
:headers => {
:quantity => 1,
:items => ["Origin"]
}
},
:trusted_signers => {
:enabled => false,
:quantity => 0
},
:viewer_protocol_policy => "allow-all",
:min_ttl => 0,
:allowed_methods => {
:quantity => 3,
:items => ["HEAD","GET","OPTIONS"],
:cached_methods => {
:quantity => 3,
:items => ["HEAD","GET","OPTIONS"]
}
},
:smooth_streaming => false,
:default_ttl => 86400,
:max_ttl => 31536000,
:compress => true
},
:cache_behaviors => {
:quantity => 0
},
:custom_error_responses => {
:quantity => 0
},
:comment => "",
logging: {
enabled: true, # required
include_cookies: false, # required
bucket: "example-logs", # required
prefix: "#{self.id}", # required
},
:price_class => "PriceClass_100",
:enabled => true,
:restrictions => {
:geo_restriction => {
:restriction_type => "none",
:quantity => 0
}
}
}
})
I compared the results I got back from an existing instance with
client = Aws::CloudFront::Client.new(:http_wire_trace => true)
resp = client.get_distribution_config({
:id => '<ID>'
})
Changing the payload from
:custom_headers => {
:quantity => 0,
:items => []
},
to
:custom_headers => {
:quantity => 0
},
seemed to fix the same error message for me.

Resources