Multiple series using pluck and chartkick? - ruby-on-rails

I have some data in a table that looks like the following:
date apples pears oranges
1 3 2 0
2 1 5 0
3 0 2 1
4 0 0 1
5 0 1 1
I can plot the count of a fruit by date using:
<%= line_chart FruitCount.pluck(:date, :apples), xtitle: "Date", ytitle: "Count" %>
I can't workout how to plot apples, pears and oranges onto the same plot. I thought it might be something like the following:
<%= line_chart [
{ name: Apples, data: FruitCount.pluck(:date, :apples) },
{ name: Pears, data: FruitCount.pluck(:date, :pears) }
{ name: Oranges, data: FruitCount.pluck(:date, :oranges) }
], xtitle: "Date", ytitle: "Count" %>
All help greatly appreciated.
Edit:
The issue was a missed comma and the names not being strings. The code below works:
<%= line_chart [
{ name: "Apples", data: FruitCount.pluck(:date, :apples) },
{ name: "Pears", data: FruitCount.pluck(:date, :pears) },
{ name: "Oranges", data: FruitCount.pluck(:date, :oranges) },
], xtitle: "Date", ytitle: "Count" %>

I have a line chart showing items that are "expiring" on a given date grouped by month using line_chart. I like the built-in feature of clicking on the color keys to show/hide each line. I use a complicated query to get info in this format:
{["Footwear", Sat, 01 May 2021]=>0,
["Footwear", Tue, 01 Jun 2021]=>1,
["Footwear", Thu, 01 Jul 2021]=>0,
...
["Coat", Sun, 01 Aug 2021]=>0,
["Coat", Wed, 01 Sep 2021]=>0,
["Coat", Fri, 01 Oct 2021]=>0,
["Coat", Mon, 01 Nov 2021]=>0,
...
["Helmet", Wed, 01 Dec 2021]=>0,
["Helmet", Sat, 01 Jan 2022]=>0,
["Helmet", Tue, 01 Feb 2022]=>1,
["Helmet", Tue, 01 Mar 2022]=>2,
["Helmet", Fri, 01 Apr 2022]=>0...}
So just hand line_chart a hash with data like: ["Apple", <date>] => count] and chartkick will do the rest. I have a very limited view of your table structures but going on what you have given us I think you could do:
hash_for_chart = {}
FruitCount.pluck(:date, :apples, :pears, :oranges).each do |arr|
hash_for_chart['Apples', arr[0]] = arr[1]
hash_for_chart['Pears', arr[0]] = arr[2]
hash_for_chart['Oranges', arr[0]] = arr[3]
end
Here we are grabbing every FruitCount record, plucking the date and the three counts so we have an array of those values. Then we add a hash pair for each of the three values on that date to a hash that we will pass to line_chart
line_chart hash_for_chart
There may be a more complex AR query that could give you the same data but hard for me to say with such limited info about your DB.

Related

xaxis populated with weekdays looping

wasted lot of hours searching up and down, but didnt found my very simple needing.
I just only need xaxis showing: (weekdays rolling looping repeating)
Mon tue wed thu wed sat sun Mon tue wed thu wed sat sun Mon tue wed thu wed sat sun Mon tue wed thu wed sat sun ....... repeating or looping the 7 days till infinite (Y has a value).
This should be very easy to make, should be 1st chapter in the first demo lines but No Way, noone treat the simple.
Thank YOU in advance
Best regards
eng G. Bono from Italy Turin.
For a categorized axis you can for example programmatically create an array with weekdays, for example:
var data = [...],
categories = ['mon', 'tue', 'wed', 'thu', 'wed', 'sat', 'sun'],
i = 7;
for (; i < data.length; i++) {
categories.push(categories[i - 7]);
}
Highcharts.chart('container', {
series: [{
data: data
}],
xAxis: {
categories: categories
}
});
Live demo: http://jsfiddle.net/BlackLabel/6m4e8x0y/4962/
For a datetime axis you need to only use the proper format:
xAxis: {
type: 'datetime',
labels: {
formatter: function() {
return Highcharts.dateFormat('%a', this.value)
}
}
}
Live demo: http://jsfiddle.net/BlackLabel/6m4e8x0y/4963/
API Reference:
https://api.highcharts.com/highcharts/xAxis.type
https://api.highcharts.com/class-reference/Highcharts#.dateFormat
https://api.highcharts.com/highcharts/xAxis.labels.formatter

Sum the next value of hash in an array of hashes

I have an array of hashes like this:
[{Mon, 09 May 2016 14:49:17 UTC +00:00=>12},
{Sun, 17 Apr 2016 14:08:40 UTC +00:00=>30},
{Sun, 16 Apr 2016 14:08:40 UTC +00:00=>18},
{Sun, 15 Apr 2016 14:03:33 UTC +00:00=>21}]
How can I sum the previous value from the oldest date to the current date, my expected output will be:
[{Mon, 09 May 2016 14:49:17 UTC +00:00=>81},
{Sun, 17 Apr 2016 14:08:40 UTC +00:00=>69},
{Sun, 16 Apr 2016 14:08:40 UTC +00:00=>39},
{Sun, 15 Apr 2016 14:03:33 UTC +00:00=>21}]
Thanks!
Assuming that the key of every hash in your array is a DateTime object, you can get what your want with this:
balance = [
{DateTime.parse('Mon, 09 May 2016 14:49:17 UTC +00:00')=>12},
{DateTime.parse('Sun, 17 Apr 2016 14:08:40 UTC +00:00')=>30},
{DateTime.parse('Sun, 16 Apr 2016 14:08:40 UTC +00:00')=>18},
{DateTime.parse('Sun, 15 Apr 2016 14:03:33 UTC +00:00')=>21}
] # => your original array
# Get expected array.
balance.map{ |h|
{
h.keys.first => balance.select{ |e|
e.keys.first <= h.keys.first }.map{ |s|
s[s.keys.first] }.reduce(:+)
}
}
I split the code in lines in order to improve readability.
Another approach would be to sort the array first and then use the map function to keep a running total to collect the required data.
# sort the balances by date
balance = balance.sort {|a, b| a.keys.first <=> b.keys.first }
# get running total and collect for each date
total = 0
balance.map do |entry|
date, value = entry.first
total += value
{date => total}
end
I assume your array is in lastest-to-earliest date order and looks something like arr below:
a = [{ "Mon, 09 May 2016 14:49:17 UTC +00:00"=>12 },
{ "Sun, 17 Apr 2016 14:08:40 UTC +00:00"=>30 },
{ "Sun, 16 Apr 2016 14:08:40 UTC +00:00"=>18 },
{ "Sun, 15 Apr 2016 14:03:33 UTC +00:00"=>21 }]
require 'date'
arr = a.map do |h|
(d, v) = h.to_a.first
{ DateTime.parse(d) => v }
end
#=> [{#<DateTime: 2016-05-09T14:49:17+00:00 ((2457518j,53357s,0n),+0s,2299161j)>=>12},
# {#<DateTime: 2016-04-17T14:08:40+00:00 ((2457496j,50920s,0n),+0s,2299161j)>=>30},
# {#<DateTime: 2016-04-16T14:08:40+00:00 ((2457495j,50920s,0n),+0s,2299161j)>=>18},
# {#<DateTime: 2016-04-15T14:03:33+00:00 ((2457494j,50613s,0n),+0s,2299161j)>=>21}]
We can then compute the required array as follows.
cumv = 0
arr.reverse.
map { |h| h.to_a.first }.
each_with_object([]) do |(d,v),a|
cumv += v
a << { d => cumv }
end.
reverse
#=> [{#<DateTime: 2016-05-09T14:49:17+00:00 ((2457518j,53357s,0n),+0s,2299161j)>=>81},
# {#<DateTime: 2016-04-17T14:08:40+00:00 ((2457496j,50920s,0n),+0s,2299161j)>=>69},
# {#<DateTime: 2016-04-16T14:08:40+00:00 ((2457495j,50920s,0n),+0s,2299161j)>=>39},
# {#<DateTime: 2016-04-15T14:03:33+00:00 ((2457494j,50613s,0n),+0s,2299161j)>=>21}]

How to print my hash value if it contains time stamp values?

I have a hash in ruby i want to print it in proper manner using hash objects like hash.each_pair(). But its not working as it has some time stamp values.
file1={:file_modify_date=>2015-01-08 12:34:34 +0530, :file_modify_date_civil=>Thu, 08 Jan 2015, :file_access_date=>2015-01-08 13:23:09 +0530, :file_access_date_civil=>Thu, 08 Jan 2015, :file_inode_change_date=>2015-01-08 12:34:34 +0530, :file_inode_change_date_civil=>Thu, 08 Jan 2015, :file_permissions=>"rw-r--r--", :file_type=>"JPEG", :mime_type=>"image/jpeg"}
file1.each_pair { |k, v| puts "Key: #{k}, Value: #{v}" }
How about you format your time_stamp value to string
file1={:file_modify_date=> time1.strftime("%Y-%d-%m %H:%M:%S +%z"), ....}
using strftime to format your time stamp
file1.each_pair { |k, v| puts "Key: #{k}, Value: #{v}" }
see also Rails strftime
Try following
file1={:file_modify_date=>'2015-01-08 12:34:34 +0530', :file_modify_date_civil=>'Thu, 08 Jan 2015', :file_access_date=>'2015-01-08 13:23:09 +0530', :file_access_date_civil=>'Thu, 08 Jan 2015', :file_inode_change_date=>'2015-01-08 12:34:34 +0530', :file_inode_change_date_civil=>'Thu, 08 Jan 2015', :file_permissions=>"rw-r--r--", :file_type=>"JPEG", :mime_type=>"image/jpeg"}
file1.each_pair { |k, v| puts "Key: #{k}, Value: #{v}" }

How do I get the first Thursday of the month in Ruby/Rails?

The following Ruby code gets me the first day of each month :
require 'active_support/all'
# get the date at the beginning of this month
date = Date.today.beginning_of_month
# get the first day of the next 5 months
5.times do |num|
date = date.next_month
p date
end
Which gives :
=> Fri, 01 Aug 2014
=> Mon, 01 Sep 2014
=> Wed, 01 Oct 2014
=> Sat, 01 Nov 2014
=> Mon, 01 Dec 2014
But how do I get the first Thursday of each month? i.e.
=> Thu, 07 Aug 2014
=> Thu, 04 Sep 2014
=> Thu, 02 Oct 2014
=> Thu, 06 Nov 2014
=> Thu, 04 Dec 2014
There's no need for iterations or conditions just get the so called delta of days till next thursday:
#4 is thursday because wday starts at 0 (sunday)
date = Date.today.beginning_of_month
date += (4 - date.wday) % 7
p date
=> Thu, 03 Jul 2014
That my opinion:
date_begin = Date.today.beginning_of_month
date_end = date_begin + 5.month
[*date_begin..date_end].select(&:thursday?).uniq(&:month)
=> [Thu, 03 Jul 2014, Thu, 07 Aug 2014, Thu, 04 Sep 2014, Thu, 02 Oct 2014, Thu, 06 Nov 2014]
Just for fun
class Date
def skip_to_thursday
# given current weekday, how many days we need to add for it to become thursday
# for example, for monday (weekday 1) it's 3 days
offset = lambda {|x| (4-x) % 7 }
self + offset[wday]
end
end
# get the date at the beginning of this month
date = Date.today.beginning_of_month
date.skip_to_thursday # => Thu, 03 Jul 2014
Here is my way :
def first_thursday
date = Date.today.beginning_of_month
date += 1 until date.wday == 4
date
end
first_thursday # => Thu, 03 Jul 2014
you can use something like this:
def first_thursday(months_ahead)
start_of_month = months_ahead.months.from_now.beginning_of_month.to_date
start_of_month += (4 - start_of_month.cwday) % 7
end
first_thursday 1
=> Thu, 07 Aug 2014
first_thursday 2
=> Thu, 04 Sep 2014
I ran into this problem for a recurring_events feature that I needed to build. I changed some of the variables to find the first Thursday but it also shows how you could evolve the answer to find the 2nd or 3rd Thursday (or any day of the week for that matter) if you had a week and day of the week count.
def find_thursday
start_of_month = DateTime.now.beginning_of_month
month_day = nil
loop do
month_day = start_of_month += 1.day
break if month_day.wday == find_weekday("Thu")
end
return month_day
end
def find_weekday
d = default_weekdays.find { |d| d[:day] == start_date.strftime("%a") }
d[:count]
end
def default_weekdays
return [
{ day: 'Sun', count: 0 },
{ day: 'Mon', count: 1 },
{ day: 'Tue', count: 2 },
{ day: 'Wed', count: 3 },
{ day: 'Thu', count: 4 },
{ day: 'Fri', count: 5 },
{ day: 'Sat', count: 6 },
]
end

Is the Time object suitable to create a calendar?

I want to make a database-backed calendar. Will the Time object make my life easier? It hasn't so far...
The .end_of_year method gives me some strange information. If it's contemporary time it works flawlessly:
date = '2012-3-2'.to_time(:utc) #=> 2012-03-02 00:00:00 UTC
date.end_of_year #=> 2012-12-31 23:59:59 UTC
However, if you go back in time things get strange.
date = '1399-3-2'.to_time(:utc) #=> 1399-03-02 00:00:00 UTC
date.end_of_year #=> 1399-12-23 23:59:59 UTC
23rd of December? Shouldn't that be 31st?
It's not even consistent:
date = '0000-3-2'.to_time(:utc) #=> 0000-03-02 00:00:00 UTC
date.end_of_year #=> 0001-01-02 23:59:59 UTC
Um, the 2nd of January? OF THE NEXT YEAR? What is going on?
Also, are leap years taken into account by the object?
You could use DateTime instead:
date = '2012-3-2'.to_datetime #=> Fri, 02 Mar 2012 00:00:00 +0000
date.end_of_year #=> Mon, 31 Dec 2012 23:59:59 +0000
date = '1399-3-2'.to_datetime #=> Sun, 02 Mar 1399 00:00:00 +0000
date.end_of_year #=> Wed, 31 Dec 1399 23:59:59 +0000
date = '0000-3-2'.to_datetime #=> Tue, 02 Mar 0000 00:00:00 +0000
date.end_of_year #=> Fri, 31 Dec 0000 23:59:59 +0000
It's mora accurate, and you can format the output
I've did some digging. Here's what I found.
Let's begin with end_of_year:
def end_of_year
change(:month => 12).end_of_month
end
Which relies on change and end_of_month:
def end_of_month
last_day = ::Time.days_in_month(month, year)
last_hour{ days_since(last_day - day) }
end
The most interesting part is happening inside of days_since:
def days_since(days)
advance(:days => days)
end
The advance method is a bit more complex:
def advance(options)
unless options[:weeks].nil?
options[:weeks], partial_weeks = options[:weeks].divmod(1)
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
end
unless options[:days].nil?
options[:days], partial_days = options[:days].divmod(1)
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
end
d = to_date.advance(options)
time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
seconds_to_advance = options.fetch(:seconds, 0) +
options.fetch(:minutes, 0) * 60 +
options.fetch(:hours, 0) * 3600
if seconds_to_advance.zero?
time_advanced_by_date
else
time_advanced_by_date.since(seconds_to_advance)
end
end
And he is the guy we're looking for :
# in rails console
time = '0000-01-01'.to_time(:utc) #=> 0000-01-01 00:00:00 UTC
time.advance(days: 1) #=> 0000-01-04 00:00:00 UTC
time.advance(days: 2) #=> 0000-01-05 00:00:00 UTC
time.advance(days: 3) #=> 0000-01-06 00:00:00 UTC
That's all for now. I will continue to dig.

Resources