ruby-on-rails - 如何将数据库结果中的行转换为列

标签 ruby-on-rails ruby postgresql

我有一个由 Postgres 支持的 Ruby 项目。我有一个表,其中有一个名为 first_ship_date 的日期字段和一个 Rails 枚举形式的交付状态。

我需要创建一个类似于以下内容的散列或数组:

[ { week: 2016-01-04,
    0: 15,
    1: 30,
    2: 8
  },
  { week: 2016-01-11,
    0: 8,
    1: 45,
    2: 37
  }
]

完成后,我将在 Google 图表中显示结果。

这是我目前所拥有的:

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)

这将返回一个数组,其值类似于:

[ [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] ]

将此原始数据转换为上面的散列/数组的最佳和最有效的方法是什么,请记住枚举的值并不总是可见的?这意味着,有几周我们没有获得所有的 delivery_status 值。

最佳答案

回答

使用 Enumerable#inject:http://ruby-doc.org/core-2.3.1/Enumerable.html#method-i-inject

一行:

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}]

多行:

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}]

证明

通过控制台:

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

多行:

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

证明

通过控制台:

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}]

关于ruby-on-rails - 如何将数据库结果中的行转换为列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38463977/

相关文章:

ruby-on-rails - 映射/收集第一个真实结果

ruby-on-rails - 如何访问 Rails 模型上的当前主机和端口?

Ruby 正则表达式闭包总是返回 false

ruby 控制台 : How to Keep 'gets' from adding a new line when the user inputs

python - 这个 psycopg2 代码可以安全地注入(inject)吗?

ruby-on-rails - 卸载并重新安装 Ruby 和 Rails 后,为什么找不到它?

php - MVC 傻瓜 : why does controller have to send anything to views?

ruby - 短路 Ruby `begin ... end` block 的正确习惯用法是什么?

postgresql - 如何使用 Docker/Kubernetes 为 PostgreSQL 故障转移集群建模?

java - 如何将双向 map 保存在数据库中?