def self.group_by(field, format = 'day')
key_op = [['year', '$year'], ['month', '$month'], ['day', '$dayOfMonth']]
key_op = key_op.take(1 + key_op.find_index { |key, op| format == key })
project_date_fields = Hash[*key_op.collect { |key, op| [key, {op => "$#{field}"}] }.flatten]
group_id_fields = Hash[*key_op.collect { |key, op| [key, "$#{key}"] }.flatten]
pipeline = [
{"$project" => {"name" => 1, field => 1}.merge(project_date_fields)},
{"$group" => {"_id" => group_id_fields, "count" => {"$sum" => "$QtyUsed"}}},
{"$sort" => {"count" => -1}}
]
collection.aggregate(pipeline)<br>
end
when I execute that script, count result is 0.
How can I sum attributes QtyUsed?
you are not projecting the "QtyUsed" field
pipeline = [
{"$project" => {"name" => 1, field => 1, "QtyUsed' => 1}.merge(project_date_fields)},
{"$group" => {"_id" => group_id_fields, "count" => {"$sum" => "$QtyUsed"}}},
{"$sort" => {"count" => -1}}
]
Related
I am using Ruby on Rails application. I want to combine 2 array of hashes with hash and to result in array of hashes.
Inputs:
first_array_of_hash = [{:name => "John", :age => 34, :mode => "nullable"},{:name => "Rose", :age => 30, :mode => "nullable"}]
second_hash = {:field_name => "", :field_age => nil, :field_nullable => false, :field_default => ""}
I want my result to be like below
result = [{:field_name => "John", :field_age => 34, :field_nullable => true, :field_default => ""},{:field_name => "Rose", :field_age => 30, :field_nullable => true, :field_default => ""}]
You can use a regular Array#map for this:
first_array_of_hash = [{:name => "John", :age => 34, :nullable => 'yes'},{:name => "Rose", :age => 30, :nullable => 'no'}]
second_hash = {:field_name => "", :field_age => nil, :field_nullable => false, :field_default => ""}
def transform(object)
{
field_name: object[:name],
field_age: object[:age],
field_nullable: object[:mode] == 'nullable'
}
end
result = first_array_of_hash.map do |object|
second_hash.merge(transform(object))
end
puts result
I got this query:
User.collection.aggregate([
{"$project" => {
"dayOfMonth" => {"$dayOfMonth" => "$created_time"},
"month" => {"$month" => "$created_time"},
"year" => {"$year" => "$created_time"},
}},
{"$group" => {
"_id" => { "dayOfMonth" => "$dayOfMonth", "month" => "$month", "year" => "$year"},
"Total" => {"$sum" => 1}
}},
{"$sort" => {
"created_at" => -1
}}
])
My user table has username and fullname which I need,
I did something like
User.collection.aggregate([
{"$project" => {
"dayOfMonth" => {"$dayOfMonth" => "$created_at"},
"month" => {"$month" => "$created_at"},
"year" => {"$year" => "$created_at"},
"username" => "$username"
}},
{"$group" => {
"_id" => { "dayOfMonth" => "$dayOfMonth", "month" => "$month", "year" => "$year"},
"Total" => {"$sum" => 1}
}},
{"$sort" => {
"created_at" => -1
}}
])
but it doesn't work and gives same result like above, doesn't print username, any ideas ?
My goal is get username, after grouping by created at date.
User.collection.aggregate([
{"$project" => {
"dayOfMonth" => {"$dayOfMonth" => "$created_at"},
"month" => {"$month" => "$created_at"},
"year" => {"$year" => "$created_at"},
"username" => "$username"
}},
{"$group" => {
"_id" => { "dayOfMonth" => "$dayOfMonth", "month" => "$month", "year" => "$year"},
"Total" => {"$sum" => 1},
"username" => {"$first" => "$username"}
}},
{"$sort" => {
"created_at" => -1
}}
])
Trying to figure out a way to get the max/min values out of a hash of hash. Example would be that I want to get the player with the highest deaths and the player with the highest kills.
Example of a hash:
{ 1234 =>
{ :steamID => 1234,
:alias => "Bob",
:kills => "50",
:deaths => "10"
},
5678 =>
{ :steamID => 5678,
:alias => "Jim",
:kills => "10",
:deaths => "12"
},
}
Trying to get an output of something like:
{ :most_kills =>
{ :steamID => 12345,
:name => "Bob",
:stat => "50"},
:most_deaths =>
{ :steamID => 12345,
:name => "Bob",
:stat => "50"
}
}
The following will find the player with the max kills and deaths stats and map the output to the desired format:
input = { 1234 =>
{ steamID: 1234,
alias: "Bob",
kills: "50",
deaths: "10"
},
5678 =>
{ steamID: 5678,
alias: "Jim",
kills: "10",
deaths: "12"
},
}
output = [:kills, :deaths].each_with_object({}) do |stat, h|
max = input.max_by {|k,v| v[stat]}
h["most_#{stat}".to_sym] = {
:steamID => max[0],
:name => max[1][:alias],
:stat => max[1][stat]
}
end
The output is:
{
:most_kills => {
:steamID => 1234,
:name => "Bob",
:stat => "50"
},
:most_deaths => {
:steamID => 5678,
:name => "Jim",
:stat => "12"
}
}
I might write it like this:
def most(input, key_name)
_, v = input.max_by { |_,v| v[key_name.to_sym] }
[("most_#{key_name}").to_sym, v]
end
["deaths", "kills"].map {|key_name| most(input, key_name) }.to_h
#=> {:most_deaths=>{:steamID=>5678, :alias=>"Jim",
# :kills=>"10", :deaths=>"12"},
# :most_kills =>{:steamID=>1234, :alias=>"Bob",
# :kills=>"50", :deaths=>"10"}}
Basically I have an array of hashes like so :
[
{ :id => 20, :total => 1, :total2 => 0 },
{ :id => 21, :total => 1, :total2 => 0 },
{ :id => 22, :total => 2, :total2 => 0 },
{ :id => 23, :total => 1, :total2 => 0 },
{ :id => 20, :total => 1, :total2 => 0 },
{ :id => 21, :total => 1, :total2 => 0 },
{ :id => 22, :total => 1, :total2 => 1 },
{ :id => 23, :total => 1, :total2 => 0 }
]
I want the array to sum the last two hash columns like so, keeping the first (:id) as an identifier:
[
{ :id => 20, :total => 2, :total2 => 0 },
{ :id => 21, :total => 2, :total2 => 0 },
{ :id => 22, :total => 3, :total2 => 1 }
]
I have looked around and it seems that the .inject() method is used in this instance but I cannot really figure out the syntax/how to use this.
What I am looking for is to keep the first column (:id) as an ID field; if there is another hash with this ID, like in my example above, the two hashes should be added together.
Can you try this?
array = [{:stemp=>20, :vtotal=>1, :avg=>0}, {:stemp=>21, :vtotal=>1, :avg=>0},{:stemp=>22, :vtotal=>2, :avg=>0}, {:stemp=>23, :vtotal=>1, :avg=>0}, {:stemp=>20, :vtotal=>1, :avg=>0}, {:stemp=>21, :vtotal=>1, :avg=>0}, {:stemp=>22, :vtotal=>1, :avg=>1}, {:stemp=>23, :vtotal=>1, :avg=>0}]
result = array.group_by{|h| h[:stemp]}.map do |stemp, hashes|
{ stemp: stemp, vtotal: hashes.map{|h| h[:vtotal]}.inject(:+), avg: hashes.map{|h| h[:avg]}.inject(:+) }
end
Just copy-pasted it in the IRB console with ruby 1.9.3, outputs this:
[
{:stemp=>20, :vtotal=>2, :avg=>0},
{:stemp=>21, :vtotal=>2, :avg=>0},
{:stemp=>22, :vtotal=>3, :avg=>1},
{:stemp=>23, :vtotal=>2, :avg=>0}
]
I reformatted the data in the question and in my answer so that it is a little easier for others to see what is going on.
data = [
{ :stemp => 20, :vtotal => 1, :avg => 0 },
{ :stemp => 21, :vtotal => 1, :avg => 0 },
{ :stemp => 22, :vtotal => 2, :avg => 0 },
{ :stemp => 23, :vtotal => 1, :avg => 0 },
{ :stemp => 20, :vtotal => 1, :avg => 0 },
{ :stemp => 21, :vtotal => 1, :avg => 0 },
{ :stemp => 22, :vtotal => 1, :avg => 1 },
{ :stemp => 23, :vtotal => 1, :avg => 0 }
]
First, group your hashes by the stemp.
data = data.group_by { |datum| datum[:stemp] }
Then iterate over each stemp and its entries.
data = data.map do |stemp, entries|
# this pulls out each hash's :vtotal entry and then combines it with the + operator
vtotal = entries.map { |entry| entry[:vtotal] }.inject(&:+)
# this does the same as above, but for the avg entry
avg = entries.map { |entry| entry[:avg] }.inject(&:+)
{ :stemp => stemp, :vtotal => vtotal, :avg => avg }
end
This outputs
[
{ :stemp => 20, :vtotal => 2, :avg => 0 },
{ :stemp => 21, :vtotal => 2, :avg => 0 },
{ :stemp => 22, :vtotal => 3, :avg => 1 },
{ :stemp => 23, :vtotal => 2, :avg => 0 }
]
This solution suffers from readability but I wanted to provide it for reference.
Hash#merge accepts a block that will be executed when a colliding key is found.
arr = [ {:id => 20, :total => 1, :total2 => 0} ... ]
arr.group_by{ |h| h[:id] }.map do |_, hash|
hash.reduce do |hash_a, hash_b|
hash_a.merge(hash_b){ |key, old, new| key == :id ? old : old + new }
end
end
This one also works
arr.group_by{|t| t[:stemp]}.map {|key, value| value.inject({}) { |hash, values| values.merge(hash){ |key, v1, v2| key == :stemp ? v1 : v1+v2 }}}
change to :id
arr.group_by{|t| t[:id]}.map {|key, value| value.inject({}) { |hash, values| values.merge(hash){ |key, v1, v2| key == :id ? v1 : v1+v2 }}}
[{"4"=>"20.0"}, {"4"=>"20.0"}, {"4"=>"10.0"}, {"4"=>"10.0", "5"=>"10.0"}, {"4"=>"10.0", "5"=>"0.0"}, {"4"=>"10.0"}, {"4"=>"10.0"}, {"4"=>"0.0", "5"=>"10.66"}, {"4"=>"20.0"}, {"4"=>"10.0"}, {"4"=>"10.0"}, {"4"=>"0.0"}].map{|m| m.map{|k,v| v.to_f}.sum()}.sum()
I'm trying to pass the following ruby hash into an active resource(3.0.9) find(:from) call.
my_hash = {
:p => {:s => 100, :e => 2},
:k => "blah",
:f => [
{
:fl => :bt,
:tp => :trm,
:vl => "A::B"
},
{
:fl => :jcni,
:tp => :trm,
:vl => [133, 134]
},
{
:mnfl => :bmns,
:mxfl => :bmxs,
:tp => :rfstv,
:vl => 1e5
},
{
:fl => :bpo,
:tp => :rftv,
:op => :eta,
:vl => 1.months.ago.strftime("%Y-%m-%d")
}
]
}
Resource.find_by_x_and_y(:all, :from => :blah, params: my_hash)
On the server side action, when I print the params hash, its all messed up. ( last 3 hashes in the array mapped to :f )
{
"f" => [
{
"fl" => "bt",
"tp" => "trm",
"vl" => "A::B"
},
{
"fl" => "jcni",
"tp" => "trm",
"vl" => [
"133",
"134"
],
"mnfl" => "bmns",
"mxfl" => "bmxs"
},
{
"tp" => "rfstv",
"vl" => "100000.0",
"fl" => "bpo",
"op" => "eta"
},
{
"tp" => "rftv",
"vl" => "2013-01-25"
}
],
"k" => "blah",
"p" => {
"e" => "2",
"s" => "100"
},
"action" => "blah",
"controller" => "x/Y",
"format" => "json"
}
my_hash.to_query gives me
f[][fl]=bt&f[][tp]=trm&f[][vl]=A%3A%3AB&f[][fl]=jcni&f[][tp]=trm&f[][vl][]=133&f[][vl][]=134&f[][mnfl]=bmns&f[][mxfl]=bmxs&f[][tp]=rfstv&f[][vl]=100000.0&f[][fl]=bpo&f[][op]=eta&f[][tp]=rftv&f[][vl]=2013-01-25&k=blah&p[e]=2&p[s]=100
which doesn't have indices, hence the mixup.
Is there a name for this type of serialization using "[]" ? Is this guaranteed to serialize/deserialize arbitrarily nested hashes/arrays/primitives faithfully ? How do I make active resource behave sanely ?