我有以下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"]
我还希望显示每个日期的所有值。
有人可以给我一些方向吗?
答案 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
没有键k
,h[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"]