用于解析给定格式的Ruby中json的最佳方法

时间:2017-11-22 01:26:54

标签: ruby

对于我的rails应用程序,以下面的格式收到SQL查询结果。

   @data= JSON.parse(request,symbolize_names: true)[:data] 
#  @data sample

 [{"time":"2017-11-14","A":0,"B":0,"C":0,"D":0,"E":0},
  {"time":"2017-11-15","A":0,"B":0,"C":0,"D":0,"E":0},
  {"time":"2017-11-16","A":2,"B":1,"C":1,"D":0,"E":1},
  {"time":"2017-11-17","A":0,"B":0,"C":1,"D":0,"E":1},
  {"time":"2017-11-20","A":0,"B":0,"C":0,"D":0,"E":0},
  {"time":"2017-11-21","A":6,"B":17,"C":0,"D":0,"E":1}]

但我想要格式为

的数据
  [{"name":"A","data":{"2017-11-16":2,"2017-11-21":6}},
  {"name":"B","data":{"2017-11-16":1,"2017-11-21":17}},
  {"name":"C","data":{"2017-11-16":1,"2017-11-17":1}},
  {"name":"D","data":{}},
  {"name":"E","data":{"2017-11-16":1,"2017-11-17":1,"2017-11-21":1}}]

在Ruby中解析这个问题的最佳方法是什么? 我尝试使用@ data.each方法,但它很冗长。

我对Ruby完全不熟悉。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:3)

奇怪的具体问题,但有点问题,所以我采取了刺。如果这是来自SQL数据库,我觉得更好的解决方案是让SQL格式化数据,而不是在ruby中转换它。

@data = JSON.parse(request,symbolize_names: true)[:data]

intermediate = {}
@data.each do |row|
  time = row.delete(:time)
  row.each do |key, val|
    intermediate[key] ||= {data: {}}
    intermediate[key][:data][time] = val if val > 0
  end
end

transformed = []
intermediate.each do |key, val|
  transformed << {name: key.to_s, data: val}
end

在此transformed的末尾将包含已转换的数据。可怕的变量名称,我讨厌必须在两个通道中执行此操作。但是得到了一些有用的东西,并认为我会分享以防它有用。

答案 1 :(得分:1)

我同意csexton的观点,看起来更好的查询来源数据将是最终的解决方案。

无论如何,这里有一个类似于csexton的解决方案,但使用嵌套的默认Hash过程来简化一些操作:

def pivot(arr, column)
  results = Hash.new do |hash, key|
    hash[key] = Hash.new(0)
  end

  arr.each do |hash|
    data = hash.dup
    pivot = data.delete(column)

    data.each_pair do |name, value|
      results[name][pivot] += value
    end
  end

  results.map { |name, data| {
    name: name.to_s,
    data: data.delete_if { |_, sum| sum.zero? }
  }}
end

pivot(@data, :time) # => [{:name=>"A", :data=>{"2017-11-16"=>2, "2017-11-21"=>6}}, ..

这是一个更“Ruby-ish”(取决于你问的对象)解决方案:

def pivot(arr, column)
  arr
    .flat_map do |hash|
      hash
        .to_a
        .delete_if { |key, _| key == column }
        .map! { |data| data << hash[column] }
    end
    .group_by(&:shift)
    .map { |name, outer| {
      name: name.to_s,
      data: outer
        .group_by(&:last)
        .transform_values! { |inner| inner.sum(&:first) }
        .delete_if { |_, sum| sum.zero? }
    }}
end

pivot(@data, :time) # => [{:name=>"A", :data=>{"2017-11-16"=>2, "2017-11-21"=>6}}, ..

坦率地说,我发现它非常难以理解,我不想支持它。 :)

答案 2 :(得分:1)

arr = [{"time":"2017-11-14","A":0,"B":0,"C":0,"D":0,"E":0},
       {"time":"2017-11-15","A":0,"B":0,"C":0,"D":0,"E":0},
       {"time":"2017-11-16","A":2,"B":1,"C":1,"D":0,"E":1},
       {"time":"2017-11-17","A":0,"B":0,"C":1,"D":0,"E":1},
       {"time":"2017-11-20","A":0,"B":0,"C":0,"D":0,"E":0},
       {"time":"2017-11-21","A":6,"B":17,"C":0,"D":0,"E":1}]

(arr.first.keys - [:time]).map do |key|
   { name: key.to_s,
     data: arr.select { |h| h[key] > 0 }.
               each_with_object({}) { |h,g| g.update(h[:time]=>h[key]) } }
end
  #=> [{:name=>"A", :data=>{"2017-11-16"=>2, "2017-11-21"=>6}},
  #    {:name=>"B", :data=>{"2017-11-16"=>1, "2017-11-21"=>17}},
  #    {:name=>"C", :data=>{"2017-11-16"=>1, "2017-11-17"=>1}},
  #    {:name=>"D", :data=>{}},
  #    {:name=>"E", :data=>{"2017-11-16"=>1, "2017-11-17"=>1, "2017-11-21"=>1}}]

请注意

arr.first.keys - [:time]
  #=> [:A, :B, :C, :D, :E]
相关问题