【问题标题】:How to convert rows to columns from database results如何将数据库结果中的行转换为列
【发布时间】:2016-07-19 16:30:22
【问题描述】:

我有一个由 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 值。

【问题讨论】:

  • 您应该在数据库查询中使用 groupcount 执行此操作,而不是在 Ruby 中。
  • 正如@Jordan 所说,在数据库中进行。它是为这类事情而设计的,通过在那里执行它可以减少网络和 Rails 主机上的负载,这是一件好事。

标签: ruby-on-rails ruby postgresql


【解决方案1】:

回答

使用Enumerable#injecthttp://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(主):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(主):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(主):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(主):168:0> x.map! {|y| [DateTime.parse(y[0]), y[1]]} => [[2016 年 1 月 4 日星期一 00:00:00 +0000, 15], [2016 年 1 月 4 日星期一 00:00:00 +0000, 30], [2016 年 1 月 4 日星期一 00:00:00 +0000 , 8], [2016 年 1 月 11 日星期一 00:00:00 +0000, 8], [2016 年 1 月 11 日星期一 00:00:00 +0000, 45], [2016 年 1 月 11 日星期一 00:00:00 + 0000, 37]] irb(main):169:0> 哈希 = x.inject({}) {|h,y| h[周 = y[0].to_date.to_s] ||= []; h[周] {"2016-01-04"=>[15, 30, 8], "2016-01-11"=>[8, 45, 37]} irb(main):170:0> hash.inject({}) {|h,(k,v)| h[k] = {周: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}]

更新

针对每个值的出现次数进行了重构。

单线:

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

【讨论】:

  • 这是我的起始数组的确切列表:[[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]]
  • 当我对我的数组运行此代码时,我得到..."2016-01-04"=&gt;{:week=&gt;"2016-01-04", 0=&gt;0, 1=&gt;2, 2=&gt;2, 3=&gt;2, 4=&gt;2, 5=&gt;2, 6=&gt;0, 7=&gt;2, 8=&gt;0, 9=&gt;2, 10=&gt;2, 11=&gt;2, 12=&gt;2, 13=&gt;2, 14=&gt;2, 15=&gt;2, 16=&gt;2, 17=&gt;2, 18=&gt;2, 19=&gt;2, 20=&gt;2, 21=&gt;2, 22=&gt;2, 23=&gt;2, 24=&gt;2, 25=&gt;2, 26=&gt;1, 27=&gt;2, 28=&gt;1, 29=&gt;2, 30=&gt;1, 31=&gt;1, 32=&gt;2, 33=&gt;2, 34=&gt;2, 35=&gt;2, 36=&gt;2, 37=&gt;1, 38=&gt;2, 39=&gt;2, 40=&gt;2, 41=&gt;2, 42=&gt;2, 43=&gt;2, 44=&gt;2, 45=&gt;2, 46=&gt;2, 47=&gt;1, 48=&gt;2, 49=&gt;0, 50=&gt;2, 51=&gt;1, 52=&gt;2, 53=&gt;2, 54=&gt;0, 55=&gt;2, 56=&gt;2, 57=&gt;2, 58=&gt;2, 59=&gt;2, 60=&gt;2, 61=&gt;1, 62=&gt;2, 63=&gt;2, 64=&gt;2,
  • ^^ 您的解决方案不使用相同的起始数据
  • 没有要查询的数据库,我的起始数据使用了字符串,这些字符串被转换为 DateTime 类和 DateTime.parse(y[0])——之后的功能相同。
  • 澄清一下,您希望结果中有唯一值吗?目前,针对您的起始数组的结果是每次出现,对吗?
猜你喜欢
  • 2013-06-20
  • 2011-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-19
  • 1970-01-01
相关资源
最近更新 更多