How to create nested map from array of maps in Clojure? - clojure

So, as a result of a database call, I get a vector of maps, which let's say look like this:
[{:make "vw", :model "vanagon", :color "blue", :year 1983}
{:make "vw", :model "vanagon", :color "red", :year 1987}
{:make "vw", :model "eurovan", :color "blue", :year 1995}
{:make "vw", :model "eurovan", :color "green", :year 1997}
{:make "geo", :model "metro", :color "blue", :year 1985}
{:make "geo", :model "metro", :color "yellow", :year 1994}]
How can I get that into a nested map using two of the fields, e.g. like this:
{"vw" {"vanagon" [{:color "blue", :year 1983}, {:color "red", :year 1987}]
"eurovan" [{:color "blue", :year 1995}, {:color "green", :year 1997}]}
"geo" {"metro" [{:color "blue", :year 1985}, {:color "yellow", :year 1994}]}}
I have been messing around with group-by and other coll functions for a couple hours, and I can't wrap my head around it and figure out a reasonable way to do this.
Thanks!

(reduce (fn [aggr {:keys [make model] :as row}]
(update-in aggr
[make model]
(fnil conj [])
(dissoc row :make :model)))
{} data)
The anonymous function does a destructuring bind. update-in rewrites bound structure. The basic idea is to use conj to add in the other elements of the row. fnil is there to specify that we want vectors (when nil is found use the empty vector as the 1st argument to conj). Results are combined in a map by reduce.

Related

Deep merging hash (adding values)

I need to merge several hashes. I mean having:
a = {:x => 72, :y => 2}
b = {:x => 34, :y => 24}
c = a.deep_merge!(b)
and c is {:x => 106, :y => 26}.
Is there a feature in Crystal like deep_merge in Ruby?
EDIT : The real hash is more complex
{"request" => {"duration" => 15100012.0, "total" => 252562.0, "per_second" => 16725.0, "bytes" => 43440664.0}, "error" => {"socket" => 0.0, "read" => 25.0, "write" => 0.0, "http" => 0.0, "timeout" => 0.0}, "latency" => {"minimum" => 234.0, "maximum" => 219466.0, "average" => 7640.0, "deviation" => 12940.0}, "percentile" => {"fifty" => 2732.0, "ninety" => 20335.0, "ninety_nine" => 65824.0, "ninety_nine_ninety" => 199536.0}}
in fact it's a json transformed in a hash with PullParser
my final goal is to add create a new hash composed by values (added) from this hash on a loop
What you are describing is not a deep merge (a recursive operation), but just a custom merge strategy.
It can be solved by calling Hash#merge with a block:
a = {:x => 72, :y => 2}
b = {:x => 34, :y => 24}
c = a.merge(b) { |_, v1, v2| v1 + v2 }
pp c # => {:x => 106, :y => 26}

Clojure update-in on two keys and two functions

I am trying to update a nested hashmap using update-in function. But I want to update value of two keys, using differents functions. For exemple:
I have this hash:
{:1 {:value 0, :active false}, :2 {:value 0, :active false}
And I want update the key :1 to:
{:1 {:value 2, :active true}, :2 {:value 0, :active false}
There is some way to do this ?
Thanks in advance
Update
Maybe I just can use assoc: (assoc my-map :1 {:value 2, :active true})
You can have more than one k/v pair with assoc:
user=> (def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})
#'user/m
user=> (update-in m [:1] assoc :value 1 :active true)
{:1 {:value 1, :active true}, :2 {:value 0, :active false}}
There are also assoc-in which works like assoc. The only difference is, that you provide a vector of keys instead of a single key. So maybe you can pipe your map through some assoc-in's.
Or you use the function update (added in 1.7):
(update {:1 {:value 0, :active false}, :2 {:value 0, :active false}
:1 (fn [{:keys [value active]]
(magic value active)))

Rails sort association proxy after new object is build

I have two models,
class User < ActiveRecord::Base
has_many :posts, -> { order('post.id') }
end
class Post < ActiveRecord::Base
belongs_to: user
end
For instance i'm having a #user and two posts associated. while doing #user.posts, the result be like.
[
[0] #<Post:0x0000000aa53a20> {
:id => 3,
:title => 'Hello World',
:comment => 'Long text comes here'
},
[1] #<Post:0x0000000aa53a41> {
:id => 5,
:title => 'Hello World 2',
:comment => 'Long text comes here too'
}
]
Now, I'm building one more extra object by doing #user.posts.build and
that the below result of doing #user.posts
[
[0] #<Post:0x0000000aa53a20> {
:id => 3,
:title => 'Hello World',
:comment => 'Long text comes here'
},
[1] #<Post:0x0000000aa53a41> {
:id => 5,
:title => 'Hello World 2',
:comment => 'Long text comes here too'
},
[2] #<Post:0x0000000aa53a50> {
:id => nil,
:title => nil,
:comment => nil
},
]
What i actually want is, to sort by object with nil first. The result should exactly look like,
[
[0] #<Post:0x0000000aa53a50> {
:id => nil,
:title => nil,
:comment => nil
},
[1] #<Post:0x0000000aa53a20> {
:id => 3,
:title => 'Hello World',
:comment => 'Long text comes here'
},
[2] #<Post:0x0000000aa53a41> {
:id => 5,
:title => 'Hello World 2',
:comment => 'Long text comes here too'
}
]
It can also be done by an custom method to sort by looping through each object. But don't want to write another method. The result should in Association Proxy and not an Array
Is it possible to achieve it in association proxy itself?
Suppose, you have the #posts variable where it contains the nil item.
#posts.sort{|i,j| i.id && j.id ? i <=> j : j.id ? -1 : 1 }
result => [nil, 3, 5]

Ruby on Rails 4 does not display content_tag :i

Why are all the content_tags display except the content_tag :i
def sign_full_tag(group, obj, name, input_type="text", size="12", popover=true)
content_tag :div, class: "row" do
content_tag :div, class: "col-md-" + size do
content_tag :div, class: "form-group left-inner-addon" + class_for_invalid_input(obj, name) do
content_tag(:i, "", class: "fa fa-user fa-fw text-muted") # NOT DISPLAY!!!
group.text_field( name,
class: "form-control" + (popover ? " popover-add" : ""),
id: name.to_s.gsub("_", "-"),
type: input_type,
placeholder: "#{t('sign_up.placeholder.' + name.to_s)}",
data: { container: "body",
toggle: "popover",
placement: "bottom",
content: (popover ? "#{t('sign_up.popover.' + name.to_s)}" : nil) })
end
end
end
end
When I render this helper I don't see tag "i" in my browser.
How should this work?
That is because only the last statement in a block is returned. You have to concat the two statements so it is returned as one string:
content_tag(:div, etc etc) do
content_tag(:i, "") + group.text_field(name, etc etc)
end

Update payment details using Authorize.net

When I update the existing subscription info using update_recurring method of autorize.net gateway then payment details (credit card number, CVV number and expiry date) are not being updated.
My code snippet is as follows:-
def create_card_subscription
credit_card = ActiveMerchant::Billing::CreditCard.new(
:first_name => params[:payment_details][:name],
:last_name => params[:payment_details][:last_name],
:number => params[:payment_details][:credit_card_number],
:month => params[:expiry_date_month],
:year => params[:expiry_date_year],
:verification_value => params[:payment_details][:cvv_code]
)
if credit_card.valid?
gateway = ActiveMerchant::Billing::AuthorizeNetGateway.new(:login => '*********', :password => '**************')
response = gateway.update_recurring(
{
"subscription.payment.credit_card.card_number" => "4111111111111111",
:duration =>{:start_date=>'2010-04-21', :occurrences=>1},
:billing_address=>{:first_name=>'xyz', :last_name=>'xyz'},
:subscription_id=>"******"
}
)
if response.success?
puts response.params.inspect
puts "Successfully charged $#{sprintf("%.2f", amount / 100)} to the credit card #{credit_card.display_number}. The Account number is #{response.params['rbAccountId']}"
else
puts response.message
end
else
#Credit Card information is invalid
end
render :action=>"card_payment"
end
Try something like this:
credit_card = ActiveMerchant::Billing::CreditCard.new({
:number => self.ccnum,
:verification_value => self.ccv,
:month => self.exp_month,
:year => self.exp_year,
:first_name => self.first_name,
:last_name => self.last_name
})
response = gateway.update_recurring({
:subscription_id => self.subscription_id,
:amount => 10000000,
:credit_card => credit_card,
:customer => {
:email => "fjdksl#jklfdsjflkd.com"
},
:billing_address => {
:first_name => self.first_name,
:last_name => self.last_name,
:address1 => self.address + " " + self.address2,
:city => self.city,
:state => self.state,
:country => "US",
:zip => self.zip
}
})