I have a Ruby project that is backed by Postgres. I have a table that has a date field called first_ship_date, and a delivery status which is a Rails enum.
I need to create a hash or array that looks something similar to the following:
[ { week: 2016-01-04,
0: 15,
1: 30,
2: 8
},
{ week: 2016-01-11,
0: 8,
1: 45,
2: 37
}
]
Once this is complete I will be displaying the result in a Google Chart.
Here is what I have so far:
SalesOrder.order("date_trunc('week', first_ship_date) ASC")
.where("delivery_status is not null")
.where("date(first_ship_date) >= ? AND date(first_ship_date) <=?", Date.new(2016,1,1), Date.new(2016,3,31))
.pluck("date_trunc('week', first_ship_date)", :delivery_status)
This returns an array with values similar to:
[ [2016-01-04 00:00:00 UTC, 0],
[2016-01-04 00:00:00 UTC, 2],
[2016-01-04 00:00:00 UTC, 2],
[2016-01-04 00:00:00 UTC, 2],
[2016-01-04 00:00:00 UTC, 2] ]
What is the best and most efficient method to convert this raw data to the hash/array above, keeping in mind the value of the enum isn't always visible? Meaning, for some weeks we don't have all of the delivery_status values.
ANSWER
Using Enumerable#inject: http://ruby-doc.org/core-2.3.1/Enumerable.html#method-i-inject
One-liner:
your_array_or_arobject.inject({}) {|h,y| h[week = y[0].to_date.to_s] ||= []; h[week] << y[1]; h}.inject({}) {|h,(k,v)| h[k] = {week: k}.merge(v.each_with_index.inject({}) {|h,(k,i)| h[i] = k; h}); h}.values
#=> [{:week=>"2016-01-04", 0=>15, 1=>30, 2=>8}, {:week=>"2016-01-11", 0=>8, 1=>45, 2=>37}]
Multiline:
hash = your_array_or_arobject.inject({}) do |h,y|
h[week = y[0].to_date.to_s] ||= []
h[week] << y[1]
h
end
hash2 = hash.inject({}) do |h,(k,v)|
h[k] = {week: k}.merge(
v.each_with_index.inject({}) do |h2,(k,i)|
h2[i] = k
h2
end
)
h
end
hash2.values
#=> [{:week=>"2016-01-04", 0=>15, 1=>30, 2=>8}, {:week=>"2016-01-11", 0=>8, 1=>45, 2=>37}]
PROOF
Via console:
irb(main):159:0> x = [
irb(main):160:1* ["2016-01-04 00:00:00 UTC", 15],
irb(main):161:1* ["2016-01-04 00:00:00 UTC", 30],
irb(main):162:1* ["2016-01-04 00:00:00 UTC", 8],
irb(main):163:1*
irb(main):164:1* ["2016-01-11 00:00:00 UTC", 8],
irb(main):165:1* ["2016-01-11 00:00:00 UTC", 45],
irb(main):166:1* ["2016-01-11 00:00:00 UTC", 37]
irb(main):167:1> ]
=> [["2016-01-04 00:00:00 UTC", 15], ["2016-01-04 00:00:00 UTC", 30], ["2016-01-04 00:00:00 UTC", 8], ["2016-01-11 00:00:00 UTC", 8], ["2016-01-11 00:00:00 UTC", 45], ["2016-01-11 00:00:00 UTC", 37]]
irb(main):168:0> x.map! {|y| [DateTime.parse(y[0]), y[1]]}
=> [[Mon, 04 Jan 2016 00:00:00 +0000, 15], [Mon, 04 Jan 2016 00:00:00 +0000, 30], [Mon, 04 Jan 2016 00:00:00 +0000, 8], [Mon, 11 Jan 2016 00:00:00 +0000, 8], [Mon, 11 Jan 2016 00:00:00 +0000, 45], [Mon, 11 Jan 2016 00:00:00 +0000, 37]]
irb(main):169:0> hash = x.inject({}) {|h,y| h[week = y[0].to_date.to_s] ||= []; h[week] {"2016-01-04"=>[15, 30, 8], "2016-01-11"=>[8, 45, 37]}
irb(main):170:0> hash.inject({}) {|h,(k,v)| h[k] = {week: k}.merge(v.each_with_index.inject({}) {|h,(k,i)| h[i] = k; h}); h}.values
=> [{:week=>"2016-01-04", 0=>15, 1=>30, 2=>8}, {:week=>"2016-01-11", 0=>8, 1=>45, 2=>37}]
UPDATE
Refactored for count of occurrences for each value.
One-liner:
your_array_or_arobject.inject({}) {|h,y| h[week = y[0].to_date.to_s] ||= []; h[week] << y[1]; h}.inject({}) {|h1,(k,v)| h1[k] = {week: k}.merge(v.inject({}) {|h2,k| h2[k] ||= 0; h2[k] += 1; h2}); h1}.values
Multiline:
hash = your_array_or_arobject.inject({}) do |h,y|
h[week = y[0].to_date.to_s] ||= []
h[week] << y[1]
h
end
hash2 = hash.inject({}) do |h1,(k,v)|
h1[k] = {week: k}.merge(
v.inject({}) do |h2,k|
h2[k] ||= 0
h2[k] += 1
h2
end
)
h1
end
hash2.values
PROOF
Via console:
irb(main):001:0> require 'date'
=> true
irb(main):002:0> x = [
irb(main):003:1* ['2016-01-04 00:00:00 UTC', 0],
irb(main):004:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):005:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):006:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):007:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):008:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):009:1* ['2016-01-04 00:00:00 UTC', 0],
irb(main):010:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):011:1* ['2016-01-04 00:00:00 UTC', 0],
irb(main):012:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):013:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):014:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):015:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):016:1* ['2016-01-04 00:00:00 UTC', 2],
irb(main):017:1* ['2016-01-04 00:00:00 UTC', 2]
irb(main):018:1> ]
=> [["2016-01-04 00:00:00 UTC", 0], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 0], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 0], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2], ["2016-01-04 00:00:00 UTC", 2]]
irb(main):019:0> x.map! {|y| [DateTime.parse(y[0]), y[1]]}
=> [[#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 0], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 0], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 0], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2], [#<DateTime: 2016-01-04T00:00:00+00:00 ((2457392j,0s,0n),+0s,2299161j)>, 2]]
irb(main):020:0> x.inject({}) {|h,y| h[week = y[0].to_date.to_s] ||= []; h[week] << y[1]; h}.inject({}) {|h1,(k,v)| h1[k] = {week: k}.merge(v.inject({}) {|h2,k| h2[k] ||= 0; h2[k] += 1; h2}); h1}.values
=> [{:week=>"2016-01-04", 0=>3, 2=>12}]
Related
I have an array representing (for example) the days of the week:
["Monday", "Tuesday", "Wed", "Thursday", "Friday", "Sat", "Sunday"]
I have a array of dictionaries of the SAME size of my first array, each Dictionary contains only one key/value:
[["temp": 11], ["temp": 12], ["temp": 13], ["temp": 14], ["temp": 15], ["temp": 16], ["temp": 17]]
Each temperature correspond to the day of the same index in the first array.
Is there a way, using the array method map(transform: (Self.Generator.Element) throws -> T), to transform my array of dictionaries into the following dictionary:
["Monday": 11, "Tuesday": 12, "Wed": 13, "Thursday": 14, "Friday": 15, "Sat": 16, "Sunday": 17]
Well, you can return only Array from map and not Dictionary. Here is an alternate way of doing this:
var test1 = [["temp": 11], ["temp": 12], ["temp": 13], ["temp": 14], ["temp": 15], ["temp": 16], ["temp": 17]]
var myArray = ["Monday", "Tuesday", "Wed", "Thursday", "Friday", "Sat", "Sunday"]
var finalArray = [Dictionary<String,Int?>]()
for day in myArray {
let itemIndex = myArray.indexOf(day)
let itemDict = test1[itemIndex!]
let value = itemDict["temp"] as Int?
let changedDict = [day : value]
finalArray.append(changedDict)
}
This is obviously work for zip:
var test1: [[String: Any]] = [["temp": 11], ["temp": 12], ["temp": 13], ["temp": 14], ["temp": 15], ["temp": 16], ["temp": 17]]
var myArray = ["Monday", "Tuesday", "Wed", "Thursday", "Friday", "Sat", "Sunday"]
let temps = test1.compactMap { $0["temp"] as? Int }
let finalArray: [[String: Int]] = zip(temps, myArray)
.map { (temp, day) in [day: temp] }
print(finalArray)
I have the following array of arrays [date, value]:
array = [[12 Mar 2015, 0], [12 Mar 2015, 5], [13 Mar 2015, 0], [14 Mar 2015, 49], [15 Mar 2015, 51], [15 Mar 2015, 10], [16 Mar 2015, 110], [17 Mar 2015, 0], [18 Mar 2015, 31], [19 Mar 2015, 47], [20 Mar 2015, 0], [21 Mar 2015, 0], [22 Mar 2015, 138], [22 Mar 2015, 10], [23 Mar 2015, 0]]
You can see that there are arrays with duplicate dates. How would one sum the values while grouping by the dates? This is what I am looking for:
array = [[12 Mar 2015, 5], [13 Mar 2015, 0], [14 Mar 2015, 49], [15 Mar 2015, 61], [16 Mar 2015, 110], [17 Mar 2015, 0], [18 Mar 2015, 31], [19 Mar 2015, 47], [20 Mar 2015, 0], [21 Mar 2015, 0], [22 Mar 2015, 148], [23 Mar 2015, 0]]
Your array of days should look like
array = [["12 Mar 2015", 0], ["12 Mar 2015", 5], ["13 Mar 2015", 0], ["14 Mar 2015", 49], ["15 Mar 2015", 51], ["15 Mar 2015", 10], ["16 Mar 2015", 110], ["17 Mar 2015", 0], ["18 Mar 2015", 31], ["19 Mar 2015", 47], ["20 Mar 2015", 0], ["21 Mar 2015", 0], ["22 Mar 2015", 138], ["22 Mar 2015", 10], ["23 Mar 2015", 0]]
grouped = array.inject(Hash.new(0)) do |result, itm|
result[itm.first] += itm.last
result
end.to_a
UPDATED
Many thanks to #nathanvda, inject({}) do |hash, [time, index]| was my mistake. In any case his solution is clearer.
array.inject({}) do |hash, item|
time, index = item.to_a
hash[time] = hash.fetch(time, 0) + index
hash
end.to_a
I'm trying write my iOS app with static data where the data will be saved in Array. But if I'm working with 4-5 items, is it OK, if is there more than 5, the compiler doesn't work. He is on the step like you can see on this screen:
And I need the informations next the name of items. Someone know where is problem, how I can fix it?
var people = [
[1, "Breta", 3],
[2, "Brunda", 3],
[3, "Antonin", 3],
[4, "Andolf", 3],
[5, "Barborka", 2],
[6, "Boruvka", 2],
[7, "Anicka", 2],
[8, "Antonin", 3],
[9, "Andolf", 3],
[10, "Barborka", 2],
[11, "Boruvka", 2],
[12, "Anicka", 2],
[13, "Antonin", 3],
[14, "Andolf", 3],
[15, "Barborka", 2],
[16, "Boruvka", 2],
[17, "Anicka", 2],
[18, "Antonin", 3],
[19, "Andolf", 3],
[20, "Barborka", 2],
[21, "Boruvka", 2],
[22, "Anicka", 2],
[23, "Antonin", 3],
[24, "Andolf", 3],
[25, "Barborka", 2],
[26, "Boruvka", 2],
[27, "Anicka", 2],
[28, "Andulka", 2]
]
You just need to declare it as [AnyObject]:
var people:[AnyObject] = [[1, "Breta", 3],[2, "Brunda", 3],[3, "Antonin", 3],[4, "Andolf", 3],[5, "Barborka", 2],[6, "Boruvka", 2],[7, "Anicka", 2],[8, "Antonin", 3],[9, "Andolf", 3],[10, "Barborka", 2],[11, "Boruvka", 2],[12, "Anicka", 2],[13, "Antonin", 3],[14, "Andolf", 3],[15, "Barborka", 2],[16, "Boruvka", 2],[17, "Anicka", 2],[18, "Antonin", 3],[19, "Andolf", 3],[20, "Barborka", 2],[21, "Boruvka", 2],[22, "Anicka", 2],[23, "Antonin", 3],[24, "Andolf", 3],[25, "Barborka", 2],[26, "Boruvka", 2],[27, "Anicka", 2],[28, "Andulka", 2]]
If you wait long enough it will finish, you just have to deal with it right now. Swift is extremely slow, this kind of stuff should be fixed in swift 1.2.
It's in beta right now with iOS 8.3.
I have the following array:
[[1, 2], [44, 1], [18395, 3]]
Which I obtained by using this code:
current_user.friends_products.where("units.primary_image_id IS NOT NULL").group_by{|u| u.creator_id}.map {|k,v| [k, v.length]}
I want to sort the array by the second value of each array from greatest to least. So, this is what I'm trying to achieve:
[[18395, 3], [1, 2], [44, 1]]
Use #sort_by with the second element descending:
x = [[1, 2], [44, 1], [18395, 3]]
x.sort_by { |a, b| -b }
#=> [[18395, 3], [1, 2], [44, 1]]
You can use this Array#sort block:
[[1, 2], [44, 1], [18395, 3]].sort { |a, b| b[1] <=> a[1] }
# => [[18395, 3], [1, 2], [44, 1]]
[[1, 2], [44, 1], [18395, 3]].sort_by(&:last).reverse
arr =[[1, 2], [44, 1], [18395, 3]]
arr.sort_by{|x,y|y}.reverse
# => [[18395, 3], [1, 2], [44, 1]]
#interactions_by_time = #report.interactions.where(interaction_type: "comment").select('EXTRACT(HOUR from interaction_time)').group('EXTRACT(HOUR FROM interaction_time)').count.sort_by {|k,v| v}.reverse
I have the active record query above, which basically groups interactions by the time they occured (hour). However, the result is something like this:
[[23.0, 23], [2.0, 17], [21.0, 16], [20.0, 11], [1.0, 11], [0.0, 9], [22.0, 7], [18.0, 5], [3.0, 5], [4.0, 4], [5.0, 4], [19.0, 4], [12.0, 3], [15.0, 3], [16.0, 2], [14.0, 1], [17.0, 1], [11.0, 1], [10.0, 1], [13.0, 1]]
This is in UTC time however, how do I make sure this is in whatever time the current_user is in?
You could use the CONVERT_TZ function to take the datetime and convert it from one timezone to another: (as an offset of UTC)
CONVERT_TZ(status_changes.created_at, '+00:00', '+08:00')
or convert it from the stored value (UTC) to a local timezone like (PST).
CONVERT_TZ(status_changes.created_at,'UTC',?) between ? and ?, 'PST, '2014-01-24', '2014-03-10'
You could then allow the user to select their timezone and match the conversions to their specific timezone.