显示JSON中每个日期的所有值

时间:2015-09-16 16:48:39

标签: ruby json haml

我有以下JSON:

{
    "groups" : [
      {
        "values": "21",
        "date": "2013-02-22"
      },
      {
        "values": "25",
        "date": "2013-02-22"
      },
      {
        "values": "20",
        "date": "2013-02-22"
      },
      {
        "values": "19",
        "date": "2013-02-22"
      },
      {
        "values": "42",
        "date": "2013-02-10"
      },
      {
        "values": "30",
        "date": "2013-02-10"
      },
      {
        "values": "11",
        "date": "2013-02-10"
      }

    ]
}

我已经在Ruby类中提取了值和日期。我想找到每个日期的“最高”和“最低”值。我该怎么做?

另外,我想为它创建并行数组。例如:

low = [12, 22, 11, 45]
high = [34, 50, 15, 60]
dates = ["2013-02-22", "2013-02-10", "2013-02-06", "2013-02-01"]

我还希望显示每个日期的所有值。

有人可以给我一些方向吗?

2 个答案:

答案 0 :(得分:1)

如果str是您的JSON字符串:

require 'json'
arr = JSON.parse(str)["groups"]
  #=> [{"values"=>"21", "date"=>"2013-02-22"},
  #    {"values"=>"25", "date"=>"2013-02-22"},
  #    {"values"=>"20", "date"=>"2013-02-22"},
  #    {"values"=>"19", "date"=>"2013-02-22"},
  #    {"values"=>"42", "date"=>"2013-02-10"},
  #    {"values"=>"30", "date"=>"2013-02-10"},
  #    {"values"=>"11", "date"=>"2013-02-10"}] 

by_date = arr.each_with_object(Hash.new {|h,k| h[k] = []}) { |g,h|
  h[g["date"]] << g["values"].to_i }
  # => {"2013-02-22"=>[21, 25, 20, 19], "2013-02-10"=>[42, 30, 11]}

dates = by_date.keys
  #=> ["2013-02-22", "2013-02-10"]     
min_vals, max_vals = *by_date.map { |_,vals| vals.minmax }
  #=> [[19, 25], [11, 42]] 
min_vals
  #=> [19, 25] 
max_vals
  #=> [11, 42] 

方法Enumerable#each_with_object接受一个参数,该参数是该方法将构造和返回的对象的初始值。它的值由第二个块变量h给出。我将该参数设置为空哈希,并使用块给出的默认值:

{|h,k| h[k] = []}

什么是&#34;默认值&#34;?这意味着如果哈希h没有键kh[k]将返回一个空数组。让我们看看这是如何运作的。

最初,h #=> {}each_with_object设置第一个块变量,g等于arr的第一个值:

g = {"values"=>"21", "date"=>"2013-02-22"}

执行块计算:

h[g["date"]] << g["values"].to_i
  #=> h["2013-02-22"] << 21

由于h没有密钥"2013-02-22"h["2013-02-22"]首先设置为等于默认值,即空数组:

h["2013-02-22"] = []

然后

h["2013-02-22"] << 21
  #=> [21] 
h #=> {"2013-02-22"=>[21]} 

arr的下一个值传递给块时:

g = {"values"=>"25", "date"=>"2013-02-22"}

h如上所述。所以现在块计算是:

h[g["date"]] << g["values"].to_i
  #=> h["2013-02-22"] << 25
  #=> [21, 25] 
h #=> {"2013-02-22"=>[21, 25]} 

此时未使用默认值,因为h有一个键"2013-02-22"

另一件事可能需要解释:&#34; splat&#34; * in:

min_vals, max_vals = *by_date.map { |_,vals| vals.minmax }

我们看到了:

by_date.map { |date, vals| vals.minmax }
  #=> [[19, 25], [11, 42]]

如果*by_date.map { |date, vals| vals.minmax }位于相等的右侧,则splat会导致[[19, 25], [11, 42]]的两个元素使用并行赋值。奇怪而精彩的splat operator需要出现在每件Rubick的技巧包里。

由于我未在块计算中使用date,因此我已通过将date替换为局部变量_来引起注意。

修改:要回答您在评论中发布的问题,请执行以下操作:

id   = [1,1,1,2,2,3,4]
high = [100,100,100,90,90,100,100]
low  = [20,20,20,10,10,30,40]

我理解你的问题,你可以先计算:

indices = id.each_with_index.to_a.uniq(&:first).map(&:last)
  #=> [0, 3, 5, 6]

然后你想要的三个数组是:

id.values_at(*indices)
  #=> [1, 2, 3, 4] 
high.values_at(*indices)
  #=> [100, 90, 100, 100] 
low.values_at(*indices)
  #=> [20, 10, 30, 40] 

答案 1 :(得分:1)

您可以group_by :date并遍历日期。然后在组中创建一个:values数组。

然后使用minmax获取正确的值,并使用transpose最终数组来获取数组并分配日期,低和高。

json = {
  "groups": [
    { "values": "21", "date": "2013-02-22" },
    { "values": "25", "date": "2013-02-22" },
    { "values": "20", "date": "2013-02-22" },
    { "values": "19", "date": "2013-02-22" },
    { "values": "42", "date": "2013-02-10" },
    { "values": "30", "date": "2013-02-10" },
    { "values": "11", "date": "2013-02-10" }
  ]
}

dates, low, high = json[:groups].group_by { |g| g[:date] }.map do |date, grouped|
  values = grouped.map { |group| group[:values] }
  [date, *values.minmax]
end.transpose
# => => [["2013-02-22", "2013-02-10"], ["19", "11"], ["25", "42"]] 

dates
# => ["2013-02-22", "2013-02-10"]
low
# => ["19", "11"]
high
# => ["25", "42"]