I have this sql query
Ticket.joins([assigned_user::departament],:ticket_status).group("ticket_statuses.name","departaments.name").where("ticket_statuses.status = ?", 1).count
what does this give me back
[ "Open", "BROWSER" ] => 2,
[ "Open", "MARKETING" ] => 5,
[ "Open", "MONITORING" ] => 2,
[ "Open", "SALES" ] => 7,
[ "Open", "ADMINISTRATION" ] => 8,
[ "Open", "COLLECTIONS" ] => 1,
[ "Open", "EMPLOYEE" ] => 8,
[ "Open", "SYSTEMS" ] => 6,
[ "Open", "QUALITY" ] => 4,
[ "Open", "TECH SUPPORT" ] => 5,
[ "Open", "STORE" ] => 2,
[ "Closed", "SYSTEMS" ] => 11,
[ "Closed", "MONITORING" ] => 7,
[ "Closed", "ADMINISTRATION" ] => 4,
[ "Closed", "QUALITY" ] => 6,
[ "Closed", "STORE" ] => 6,
[ "Closed", "EMPLOYEE" ] => 2,
[ "Closed", "SALES" ] => 3,
[ "Closed", "TECHNICAL SUPPORT" ] => 4,
[ "Closed", "BROWSER" ] => 1,
[ "Closed", "COLLECTIONS" ] => 2,
[ "Closed", "MARKETING" ] => 1,
[ "Attended", "QUALITY" ] => 3,
[ "Served", "EMPLOYEE" ] => 3,
[ "Served", "WAREHOUSE" ] => 5,
[ "Attended", "COLLECTIONS" ] => 3,
[ "Served", "SYSTEMS" ] => 8,
[ "Served", "SALES" ] => 5,
[ "Attended", "TECHNICAL SUPPORT" ] => 3,
[ "Attended", "BROWSER" ] => 8,
[ "Attended", "ADMINISTRATION" ] => 7,
[ "Attended", "MONITORING" ] => 3,
[ "Served", "MARKETING" ] => 2,
[ "In process", "EMPLOYEE" ] => 4,
[ "In process", "COLLECTIONS" ] => 2,
[ "In process", "QUALITY" ] => 7,
[ "In process", "MONITORING" ] => 4,
[ "In process", "BROWSER" ] => 4,
[ "In progress", "MARKETING" ] => 4,
[ "In process", "SYSTEMS" ] => 3,
[ "In process", "ADMINISTRATION" ] => 4,
[ "In process", "TECHNICAL SUPPORT" ] => 3,
[ "In process", "WAREHOUSE" ] => 3,
[ "In process", "SALES" ] => 1
I am using this query with the 'chartkick' gem
that groups them in a graph by area, where when passing the mouse in each area it shows me this
Marketing
Open:5
Close:1
Attended:2
In process:4
Monitoring
Open:2
Close:7
Attended:3
In process:4
I would like to add to that information the total amount
and what is displayed like this
Marketing
Open:5
Close:1
Attended:2
In process:4
Total:12
Monitoring
Open:2
Close:7
Attended:3
In process:4
Total:16
I was looking for the solution but I don't know how to achieve this
Given your current output as h
h = {[ "Open", "BROWSER" ] => 2,[ "Open", "MARKETING" ] => 5,[ "Open", "MONITORING" ] => 2,[ "Open", "SALES" ] => 7,[ "Open", "ADMINISTRATION" ] => 8,[ "Open", "COLLECTIONS" ] => 1,[ "Open", "EMPLOYEE" ] => 8,[ "Open", "SYSTEMS" ] => 6,[ "Open", "QUALITY" ] => 4,[ "Open", "TECH SUPPORT" ] => 5,[ "Open", "STORE" ] => 2,[ "Closed", "SYSTEMS" ] => 11, [ "Closed", "MONITORING" ] => 7,[ "Closed", "ADMINISTRATION" ] => 4,[ "Closed", "QUALITY" ] => 6,[ "Closed", "STORE" ] => 6,[ "Closed", "EMPLOYEE" ] => 2,[ "Closed", "SALES" ] => 3,[ "Closed", "TECHNICAL SUPPORT" ] => 4,[ "Closed", "BROWSER" ] => 1,[ "Closed", "COLLECTIONS" ] => 2,[ "Closed", "MARKETING" ] => 1,[ "Attended", "QUALITY" ] => 3,[ "Served", "EMPLOYEE" ] => 3,[ "Served", "WAREHOUSE" ] => 5,[ "Attended", "COLLECTIONS" ] => 3,[ "Served", "SYSTEMS" ] => 8,[ "Served", "SALES" ] => 5,[ "Attended", "TECHNICAL SUPPORT" ] => 3,[ "Attended", "BROWSER" ] => 8,[ "Attended", "ADMINISTRATION" ] => 7,[ "Attended", "MONITORING" ] => 3,[ "Served", "MARKETING" ] => 2,[ "In process", "EMPLOYEE" ] => 4,[ "In process", "COLLECTIONS" ] => 2,[ "In process", "QUALITY" ] => 7,[ "In process", "MONITORING" ] => 4,[ "In process", "BROWSER" ] => 4,[ "In progress", "MARKETING" ] => 4,[ "In process", "SYSTEMS" ] => 3,[ "In process", "ADMINISTRATION" ] => 4,[ "In process", "TECHNICAL SUPPORT" ] => 3,[ "In process", "WAREHOUSE" ] => 3,[ "In process", "SALES" ] => 1}
The following will produce a Hash with the desired data structure:
h.each_with_object(Hash.new {|h,k| h[k] = {"Total"=> 0}}) do |((status,department),v),obj|
obj[department][status] = v
obj[department]["Total"] += v
end
# {"BROWSER"=>{"Total"=>15, "Open"=>2, "Closed"=>1, "Attended"=>8, "In process"=>4},
# "MARKETING"=>{"Total"=>12, "Open"=>5, "Closed"=>1, "Served"=>2, "In progress"=>4},
# "MONITORING"=>{"Total"=>16, "Open"=>2, "Closed"=>7, "Attended"=>3, "In process"=>4},
# "SALES"=>{"Total"=>16, "Open"=>7, "Closed"=>3, "Served"=>5, "In process"=>1},
# "ADMINISTRATION"=>{"Total"=>23, "Open"=>8, "Closed"=>4, "Attended"=>7, "In process"=>4},
# "COLLECTIONS"=>{"Total"=>8, "Open"=>1, "Closed"=>2, "Attended"=>3, "In process"=>2},
# "EMPLOYEE"=>{"Total"=>17, "Open"=>8, "Closed"=>2, "Served"=>3, "In process"=>4},
# "SYSTEMS"=>{"Total"=>28, "Open"=>6, "Closed"=>11, "Served"=>8, "In process"=>3},
# "QUALITY"=>{"Total"=>20, "Open"=>4, "Closed"=>6, "Attended"=>3, "In process"=>7},
# "TECH SUPPORT"=>{"Total"=>5, "Open"=>5}, "STORE"=>{"Total"=>8, "Open"=>2, "Closed"=>6},
# "TECHNICAL SUPPORT"=>{"Total"=>10, "Closed"=>4, "Attended"=>3, "In process"=>3},
# "WAREHOUSE"=>{"Total"=>8, "Served"=>5, "In process"=>3}}
Suppose the given hash were as follows.
h = {
["Open", "BROWSER"] =>2, ["Open", "MARKETING"] =>5,
["Open", "MONITORING"] =>2, ["Closed", "MARKETING"]=>1,
["Closed", "MONITORING"] =>7, ["Attended", "BROWSER"]=>8,
["Attended", "MONITORING"]=>3, ["Closed", "BROWSER"] =>1,
["Served", "MARKETING"] =>2
}
We may compute the desired hash as follows.
h.each_with_object({}) do |((status, dept), v), g|
g.update(dept=>{ status=>v }) { |_k,o,n| o.merge(n) }
end.transform_values { |f| f.update("Total"=>f.values.sum) }
#=> { "BROWSER"=>{"Open"=>2, "Attended"=>8, "Closed"=>1, "Total"=>11},
# "MARKETING"=>{"Open"=>5, "Closed"=>1, "Served"=>2, "Total"=>8},
# "MONITORING"=>{"Open"=>2, "Closed"=>7, "Attended"=>3, "Total"=>12}}
See:
array decomposition to better understand the way each_with_object's block variables are written;
the form of Hash#update (a.k.a. Hash#merge!) that takes a block (here { |_k,o,n| o.merge(n) }) that returns the values of keys that are present in both hashes being merged. _k holds the common key, which here is not used in the block calculation (as I've indicated by the underscore). o and n respectively hold the values of the "old" and "new" values of _k. o is the value in the hash being constructed, n is the value in the hash being merged; and
Hash#transform_values.
Note the intermediate hash that is computed:
h.each_with_object({}) do |((status, dept), v), g|
g.update(dept=>{ status=>v }) { |_k,o,n| o.merge(n) }
end
#=> { "BROWSER"=>{"Open"=>2, "Attended"=>8, "Closed"=>1},
# "MARKETING"=>{"Open"=>5, "Closed"=>1, "Served"=>2},
# "MONITORING"=>{"Open"=>2, "Closed"=>7, "Attended"=>3}}
I chose to compute the values of "Total" as a separate (second) step, to both simplify the calculations and facilitate testing. If desired, however, one could modify the code above to compute the desired hash in a single pass through the elements of h (as #engineersmnky did) as follows.
h.each_with_object({}) do |((status, dept), v), g|
g.update(dept=>{ status=>v, "Total"=>v }) do |_k,o,n|
n["Total"] += o["Total"]
o.merge(n)
end
end
Here is a second way to perform the calculation, using Enumerable#group_by.
h.group_by { |(status, dept), _v| dept }
.transform_values do |arr|
arr.each_with_object({ "Total"=>0 }) do |((status, _dept), v), g|
g.update(status=>v, "Total"=>g["Total"] + v)
end
end
#=> {"BROWSER"=>{"Total"=>11, "Open"=>2, "Attended"=>8, "Closed"=>1},
# "MARKETING"=>{"Total"=>8, "Open"=>5, "Closed"=>1, "Served"=>2},
# "MONITORING"=>{"Total"=>12, "Open"=>2, "Closed"=>7, "Attended"=>3}}
Note the intermediate calculation.
h.group_by { |(status, dept), _v| dept }
#=> { "BROWSER"=>[
# [["Open", "BROWSER"], 2], [["Attended", "BROWSER"], 8],
# [["Closed", "BROWSER"], 1]
# ],
# "MARKETING"=>[
# [["Open", "MARKETING"], 5], [["Closed", "MARKETING"], 1],
# [["Served", "MARKETING"], 2]
# ],
# "MONITORING"=>[
# [["Open", "MONITORING"], 2], [["Closed", "MONITORING"], 7],
# [["Attended", "MONITORING"], 3]
# ]
# }
You should be able to build this structure directly in the database using grouping sets. I'm assuming postgresql here (read more here https://www.postgresql.org/docs/15/queries-table-expressions.html#QUERIES-GROUPING-SETS) but this should also work for other Databases.
Ticket
.select("ticket_statuses.name as status_name, departments.name as department_name, count(*) as count")
.joins([assigned_user: :departament],:ticket_status)
.group("grouping sets ((ticket_statuses.name, departments.name), (departments.name), ())")
.where("ticket_statuses.status = ?", 1)
You will get:
Count grouped by status/department
Count grouped by department
Total Count
But you will get it as instances of your model (Ticket).
Total Count will be the one with nil for status/department and grouped by department will be the ones with nil for status.
You can tweak the order or extract the values.
I am trying to load data from redis db. I have a api only rails app and trying to render the json data as per requirement.
Currently I am able to get the data from redis in the following format.
[
{
"id": 1,
"name": "Stephenie Meyer",
"created_at": "2018-04-17T07:40:50.417Z",
"updated_at": "2018-04-17T07:40:50.417Z"
},
{
"id": 2,
"name": "V.C. Andrews",
"created_at": "2018-04-17T07:40:50.613Z",
"updated_at": "2018-04-17T07:40:50.613Z"
},
{
"id": 3,
"name": "Sophie Kinsella",
"created_at": "2018-04-17T07:40:50.646Z",
"updated_at": "2018-04-17T07:40:50.646Z"
}
]
How can convert this in a way such that the key value pairs of name,created and updated will be hash to a id key-value pair.
Into this
{"id": 1,
{
"name": "Stephenie Meyer",
"created_at": "2018-04-17T07:40:50.417Z",
"updated_at": "2018-04-17T07:40:50.417Z"
}
}
helper method for getting redis data.
def fetch_authors
authors = $redis.get('authors')
if authors.nil?
authors = Author.all.to_json
$redis.set("authors", authors).to_json
$redis.expire("authors", 5.hour.to_i)
end
JSON.load authors
end
And displaying on index page using
def index
#authors = fetch_authors
render json: #authors
end
The closest to what you want would probably be:
input = ...
input.map { |hash| [hash.delete(:id) || hash.delete('id'), hash] }.to_h
#⇒ {{1=>{:name=>...},
# {2=>{:name=>...},
# {3=>{:name=>...}}
Not exactly what you want because that's not correct syntax but you can achieve something similar with group_by
arr = [
{
"id": 1,
"name": "Stephenie Meyer",
"created_at": "2018-04-17T07:40:50.417Z",
"updated_at": "2018-04-17T07:40:50.417Z"
},
{
"id": 2,
"name": "V.C. Andrews",
"created_at": "2018-04-17T07:40:50.613Z",
"updated_at": "2018-04-17T07:40:50.613Z"
},
{
"id": 3,
"name": "Sophie Kinsella",
"created_at": "2018-04-17T07:40:50.646Z",
"updated_at": "2018-04-17T07:40:50.646Z"
}
]
arr.group_by { |e| e[:id] }
This will return
{
1 => [
{
:id => 1,
:name => "Stephenie Meyer",
:created_at => "2018-04-17T07:40:50.417Z",
:updated_at => "2018-04-17T07:40:50.417Z"
}
],
2 => [
{
:id => 2,
:name => "V.C. Andrews",
:created_at => "2018-04-17T07:40:50.613Z",
:updated_at => "2018-04-17T07:40:50.613Z"
}
],
3 => [
{
:id => 3,
:name => "Sophie Kinsella",
:created_at => "2018-04-17T07:40:50.646Z",
:updated_at => "2018-04-17T07:40:50.646Z"
}
]
}
Sorry for the bad English Title but the code below would make more sense.
I used .each to get some keys and values that I wanted from the data but when I do it I get this result.
[
[
{
id: 1,
name: "Immad",
age: 18
}
],
[
{
id: 2,
name: "Vicky",
age: 21
}
],
[
{
id: 3,
name: "Adam",
age: 24
}
]
]
I want to map the objects to some js library for which I want it to be like:
[
{
id: 1,
name: "Immad",
age: 18
},
{
id: 2,
name: "Vicky",
age: 21
},
{
id: 3,
name: "Adam",
age: 24
}
]
Can anybody please atleast give me hint what should I use in order to do it using ruby. Thanks in advance.
There already is an Array method.
foo_array.flatten!
or non destructive (just a return value foo_array remains unchanged)
foor_array.flatten
Simple, You should do like this
Test = [[{:id=>1, :name=>"Immad", :age=>18}], [{:id=>2, :name=>"Vicky", :age=>21}], [{:id=>3, :name=>"Adam", :age=>24}]]
Use .flatten here.
Test.flatten
=> [{:id=>1, :name=>"Immad", :age=>18}, {:id=>2, :name=>"Vicky", :age=>21}, {:id=>3, :name=>"Adam", :age=>24}]
Use .map!:
foo = [
[
{
id: 1,
name: "Immad",
age: 18
}
],
[
{
id: 2,
name: "Vicky",
age: 21
}
],
[
{
id: 3,
name: "Adam",
age: 24
}
]
]
# Call .map! on the array
foo.map! { |array| array.first }
=> [
[0] {
:id => 1,
:name => "Immad",
:age => 18
},
[1] {
:id => 2,
:name => "Vicky",
:age => 21
},
[2] {
:id => 3,
:name => "Adam",
:age => 24
}
]
I have some data in this format
[{
"_id" => "20",
"value" => 1
}, {
"_id" => "19",
"value" => 1
}, {
"_id" => nil,
"value" => 8
}, {
"_id" => "27",
"value" => 1
}, {
"_id" => "25",
"value" => 3
}, {
"_id" => "28",
"value" => 1
}]
I want to merge the same values with "_id" key and sum the "value" values.
Desire output
[{
"_id" => "20",
"value" => 1
}, {
"_id" => "19",
"value" => 2
}, {
"_id" => nil,
"value" => 8
}, ...]
There is an elegant way to do this?
I have tried with two loops but I think that is not the best way to do it.
As with most things in Ruby, a trip to the Enumerable documentation turns up the group_by method which can help group things together by some arbitrary criteria. Combine that with something that does the sums and you get this:
v.group_by do |e|
e['_id']
end.map do |id, list|
{
'_id' => id,
'value' => list.inject(0) { |sum, e| sum + e['value'] }
}
end
# => [{"_id"=>"20", "value"=>1}, {"_id"=>"19", "value"=>2}, {"_id"=>nil, "value"=>28},
# {"_id"=>"27", "value"=>1}, {"_id"=>"25", "value"=>3}, {"_id"=>"28", "value"=>1},
# {"_id"=>"23", "value"=>1}, {"_id"=>"16", "value"=>1}, {"_id"=>"18", "value"=>2},
# {"_id"=>"22", "value"=>2}]
arr = [{ "_id" => "20", "value" => 1 },
{ "_id" => "19", "value" => 1 },
{ "_id" => nil, "value" => 8 },
{ "_id" => "20", "value" => 1 },
{ "_id" => "25", "value" => 3 },
{ "_id" => "19", "value" => 1 },
]
h = arr.each_with_object(Hash.new(0)) { |g,h| h[g["_id"]] += g["value"] }
#=> {"20"=>2, "19"=>2, nil=>8, "25"=>3}
If you instead want to return an array of hashes with unique values for "_id" and the values of "value" updated, you could first compute h above, then
arr.uniq { |g| g["_id"] }.map { |g| g.update("_id"=>h[g["_id"]]) }
#=> [{"_id"=>"20", "value"=>2}, {"_id"=>" 19", "value"=>2},
# {"_id"=>nil, "value"=>8}, {"_id"=>"25", "value"=>3}]
This uses the methods Array#uniq with a block, Enumerable#map and Hash#update (aka merge!).
Alternatively, you could write the following.
arr.each_with_object({}) { |g,h|
h.update(g["_id"]=>g) { |_,o,n| o.merge("value"=>o["value"]+n["value"]) } }.values
#=> [{"_id"=>"20", "value"=>2}, {"_id"=>" 19", "value"=>2},
# {"_id"=>nil, "value"=>8}, {"_id"=>"25", "value"=>3}]
Again, I've used Hash#update, but this time I have employed a block to determine the values of keys that are present in both hashes being merged. See also Enumerable#each_with_object and Hash#merge. Note that, as arguments, (k=>v) is shorthand for ({ k=>v }).
I have the following json,
{
"items": [
{
"name": "table",
"item_group": [
{
"code": "code1",
"section": [
{
"unit": "centimeter",
"values": [
{
"display": "151.13 centimeter"
}
]
},
{
"unit": "centimeter (qualifier value)",
"values": [
{
"display": "170.39 centimeter"
}
]
}
],
"total": 2
}
],
"more_results": false,
"total": 1
}
]
}
How do I iterate through this and create an array of "values". I want something like [151.13 centimeter, 170.39 centimeter,....]. Please show some direction in this. thanks.
Note: I have to do this in Haml.
I know there are multiple ways of doing this. But if you really have many complex JSON queries, you can try ruby-jq gem. It is very fast, as it uses linux pipes
require 'jq'
require 'jq/extend'
json_content = {
"items": [
{
"name": "table",
"item_group": [
{
"code": "code1",
"section": [
{
"unit": "centimeter",
"values": [
{
"display": "151.13 centimeter"
}
]
},
{
"unit": "centimeter (qualifier value)",
"values": [
{
"display": "170.39 centimeter"
}
]
}
],
"total": 2
}
],
"more_results": false,
"total": 1
}
]
}
jq_filter = ' .items | .[].item_group |.[].section| . [ ] .values | . [ ] .display'
final_array = json_content.jq(jq_filter)
# => final_array = ["151.13 centimeter", "170.39 centimeter"]
Assuming you know the structure of the underlying hash, you can do this:
Code
require 'json'
JSON.parse(json_str)['items'].each_with_object([]) do |g,arr|
g['item_group'].each do |gg|
gg['section'].each do |ggg|
ggg['values'].each { |gggg|arr << gggg['display'][/\d+\.\d+/].to_f }
end
end
end
#=> [151.13, 170.39]
Explanation
The steps are as follows:
a = JSON.parse(json_str)
b = a['items']
enum = b.each_with_object([])
#=> #<Enumerator: [{"name"=>"table",..."total"=>1}]:each_with_object([])>
We can see the elements of the enumerator by converting it to an array:
enum.to_a
#=> [[{"name"=>"table", "item_group"=>[{"code"=>"code1",
# "section"=>[{"unit"=>"centimeter",
# "values"=>[{"display"=>"151.13 centimeter"}]},
# { "unit"=>"centimeter (qualifier value)",
# "values"=>[{"display"=>"170.39 centimeter"}]}], "total"=>2}],
# "more_results"=>false,
# "total"=>1},
# []]]
Notice that this array has a single element, a two-element array containing a hash and an empty array.
The first element of enum is passed to the block and assigned to the block variables using parallel assignment:
g, arr = enum.next
#=> {"name"=>"table",
# "item_group"=>[
# {"code"=>"code1",
# "section"=>[
# {"unit"=>"centimeter",
# "values"=>[
# {"display"=>"151.13 centimeter"}
# ]
# },
# {"unit"=>"centimeter (qualifier value)",
# "values"=>[
# {"display"=>"170.39 centimeter"}
# ]
# }
# ],
# "total"=>2}],
# "more_results"=>false,
# "total"=>1}
arr #=> []
c = g['item_group']
#=> [{"code"=>"code1",
# "section"=>[
# {"unit"=>"centimeter",
# "values"=>[
# {"display"=>"151.13 centimeter"}
# ]
# },
# {"unit"=>"centimeter (qualifier value)",
# "values"=>[
# {"display"=>"170.39 centimeter"}
# ]
# }
# ],
# "total"=>2}]
Note c has the form [hash].
The first (and only) element of c is passed to its block and assigned to its block variable:
gg = c.first
d = gg['section']
#=> [{"unit"=>"centimeter",
"values"=>[
{"display"=>"151.13 centimeter"}
]
},
{"unit"=>"centimeter (qualifier value)",
"values"=>[
{"display"=>"170.39 centimeter"}
]
}
]
ggg = d.first
#=> {"unit"=>"centimeter",
# "values"=>[
# {"display"=>"151.13 centimeter"}
# ]
# }
e = ggg['values']
#=> [{"display"=>"151.13 centimeter"}]
gggg = e.first
#=> {"display"=>"151.13 centimeter"}
f = gggg['display']
#=> "151.13 centimeter"
g = f[/\d+\.\d+/]
#=> "151.13"
i = g.to_f
#=> 151.13
arr << i
#=> [151.13]
arr
#=> [151.13]
The remaining calculations are similar.
Alternative expression
If, as in the example, the arrays JSON.parse(json_str)['items'], g['item_group'] and ggg['values'] each contain a single element (a hash), you could instead write:
JSON.parse(json_str)['items'].first['item_group'].first['section'].
each_with_object([]) do |g, arr|
arr << g['values'].first['display'][/\d+\.\d+/].to_f
end
#=> [151.13, 170.39]
though I doubt that would be significantly more efficient.