创建散列并检查密钥是否已存在
Creating hash and checking if the key already exists
我的方法中可以使用以下数据:
来自第一次服务调用的数据:
date: 2015-04-01
my_array = [{Apple: 3}, {Banana: 2}, {Oranges: 4}]
来自第二次服务调用的数据:
date: 2015-04-05
my_array = [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}]
在该方法的最后,我想 return 一个散列数组,其中包含从多个服务调用中收集的数据。
逻辑应该检查该键是否已存在于哈希中,如果是,则将值添加到现有键,如果不存在,则为该新键创建一个键值对象。对于这个例子,我在第一次服务调用后的哈希看起来像:
my_final_array = [{Apple: [2015-04-01, 3]}, {Banana: [2015-04-01, 2]}, {Oranges: [2015-04-01, 4]}]
然而,在我们从第二次服务调用中获取数据后,我希望我的最终数组为:
my_final_array = [{Apple: [[2015-04-01, 3], [2015-04-05, 4]]}, {Banana: [[2015-04-01, 2], [2015-04-05, 5]]}, {Oranges: [[2015-04-01, 4], [2015-04-05, 1]]}, {Kiwi: [2015-04-05, 3]}]
有没有一种简单的方法可以获得我所期望的?
我拥有的算法对数据进行两次迭代,即创建一个数组以从所有服务调用中收集数据,然后对数组进行迭代以按键分组。
这是我最初尝试解决它的方法:
dates_array.each do |week_date|
my_array = #Collect data returned by service for each week_date.
my_array.each do |sample_data|
sample_array << [date, sample_data.keys.first, sample_data.values.first]
end
end
sample_hash = sample_array.each_with_object({}) { |data_value, key_name|
(key_name[data_value[1]] ||= []) << data_value.values_at(0,2)
}
#Convert sample_hash to my_final_array for third party input.
当您有这些特定要求时,最好只创建您自己的 class - 这样您就可以在内部存储数据,但这是最好的。例如
class FunkyThing
def initialize
@s = {}
end
def add date, arr
arr.each do |e|
k, v = e.flatten
( @s[k] ||= [] ) << [ date, v ]
end
end
def val
@s.map { |k, v| { k => v } }
end
end
那么:
[142] pry(main)> a = FunkyThing.new
=> #<FunkyThing:0x007fbc23ed5cb0 @s={}>
[143] pry(main)> a.add '2015-04-01', [{Apple: 3}, {Banana: 2}, {Oranges: 4}]
=> [{:Apple=>3}, {:Banana=>2}, {:Oranges=>4}]
[144] pry(main)> a.val
=> [{:Apple=>[["2015-04-01", 3]]}, {:Banana=>[["2015-04-01", 2]]}, {:Oranges=>[["2015-04-01", 4]]}]
[145] pry(main)> a.add '2015-04-05', [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}]
=> [{:Apple=>4}, {:Banana=>5}, {:Oranges=>1}, {:Kiwi=>3}]
[146] pry(main)> a.val
=> [{:Apple=>[["2015-04-01", 3], ["2015-04-05", 4]]}, {:Banana=>[["2015-04-01", 2], ["2015-04-05", 5]]}, {:Oranges=>[["2015-04-01", 4], ["2015-04-05", 1]]}, {:Kiwi=>[["2015-04-05", 3]]}]
[147] pry(main)>
请注意,第一个输出与您在问题中要求的不同,因为这些值已经嵌套在第二层,我认为这可能是您想要的,所以我将其保留原样。
可能是这样的:
array_of_possible_keys.each do |key|
if my_final_hash.has_key?(key)
do something
else
do other thing
end
end
如果您改用散列,则不必遍历数组。而且我看不出有任何理由不更换
my_array = [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}]
my_final_array = [{Apple: [2015-04-01, 3]}, {Banana: [2015-04-01, 2]}, {Oranges: [2015-04-01, 4]}]
和
my_hash= {Apple: 4, Banana: 5, Oranges: 1, Kiwi: 3}
my_final_hash = {Apple: [2015-04-01, 3], Banana: [2015-04-01, 2], Oranges: [2015-04-01, 4]}
这是一个函数,它接受数组的当前版本、日期和要处理的新数组。
如果是第一次服务调用,则根据参数时间和要处理的数组创建一个新的数组。对于后续的服务调用,将根据数组的当前版本创建散列,然后处理参数(新)数组以将值添加到散列中。最后,哈希被转换回其原始数组形式。
请参考以下示例代码:
解决方案
def process_array(old_array: nil, date: date, my_array: my_array)
unless old_array
# service call # 1
my_array.each do |key_value_pair|
pair = key_value_pair.to_a.first
key = pair[0]
value = pair[1]
key_value_pair[key] = [date, value]
end
return my_array
else
# service call # 2 onwards
hash = {}
old_array.each do |key_value_pair|
pair = key_value_pair.to_a.first
key = pair[0]
value = pair[1]
hash[key] = value
end
my_array.each do |key_value_pair|
pair = key_value_pair.to_a.first
key = pair[0]
value = pair[1]
if hash.has_key?(key)
unless hash[key].first.kind_of?(Array)
hash[key] = [hash[key]]
end
hash[key] << [date, value]
else
hash[key] = [date, value]
end
end
output_array = []
hash.each do |key, value|
new_hash = {}
new_hash[key] = value
output_array << new_hash
end
output_array
end
end
用法
service_1 = [{Apple: 3}, {Banana: 2}, {Oranges: 4}]
array_1 = process_array(old_array: nil, date: "2015-04-01", my_array: service_1)
puts array_1.to_s
# => [{:Apple=>["2015-04-01", 3]}, {:Banana=>["2015-04-01", 2]}, {:Oranges=>["2015-04-01", 4]}]
service_2 = [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}]
array_2 = process_array(old_array: array_1, date: "2015-04-05", my_array: service_2)
puts array_2.to_s
# => [{:Apple=>[["2015-04-01", 3], ["2015-04-05", 4]]}, {:Banana=>[["2015-04-01", 2], ["2015-04-05", 5]]}, {:Oranges=>[["2015-04-01", 4], ["2015-04-05", 1]]}, {:Kiwi=>["2015-04-05", 3]}]
如果您像这样存储数据:
data1 = [{ date: "2015-04-01",
my_array: [{Apple: 3}, {Banana: 2}, {Oranges: 4}] },
{ date: "2015-04-05",
my_array: [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}] }]
考虑将其更改为:
data2 = data1.map { |g|
{ date: g[:date],
my_hash: Hash[g[:my_array].flat_map(&:to_a)] }
}
#=> [{:date=>"2015-04-01",
# :my_hash=>{:Apple=>3, :Banana=>2, :Oranges=>4}},
# {:date=>"2015-04-05",
# :my_hash=>{:Apple=>4, :Banana=>5, :Oranges=>1, :Kiwi=>3}}]
我不知道这是否更适合您的目的,但我希望您能看到它。然后你可以得到想要的分组如下:
result = data2.each_with_object({}) do |g,h|
g[:my_hash].each do |k,v|
h.update(k=>[g[:date],v]) do |_,o,n|
case o.first
when Array then o.concat(n)
else [o,n]
end
end
end
end
#=> {:Apple=> [["2015-04-01", 3], ["2015-04-05", 4]],
# :Banana=> [["2015-04-01", 2], ["2015-04-05", 5]],
# :Oranges=>[["2015-04-01", 4], ["2015-04-05", 1]],
# :Kiwi=> ["2015-04-05", 3]}
嗯,不,这不是您所要求的,但我希望您也看到它,如果您发现它是一种更有用的数据结构。将其转换为您所要求的内容很容易,我将在下面进行转换,但首先,我想解释一下有关上述计算的一些事情。
result
的计算采用 Hash#update(又名 merge!
)的形式,它使用块来确定要合并的两个哈希中存在的键的值。块变量是 k,o,n
,其中:
k
是公共密钥(我已将其更改为 _
以表示它未在块中使用);
o
(for "old")是h
中k
的值,正在构造的hash;和
n
(表示“new”)是g
中k
的值,被合并的散列。
如果你想让上面的:Kiwi
的值变成[["2015-04-05", 3]]
(我认为这样在处理结果时会更容易),将update
简化为:
h.update(k=>[[g[:date],v]]) { |_,o,n| o+n }
要将 result
转换为您要求的形式:
result.map { |k,a| { k=>a } }
#=> [{:Apple=> [["2015-04-01", 3], ["2015-04-05", 4]]},
# {:Banana=> [["2015-04-01", 2], ["2015-04-05", 5]]},
# {:Oranges=>[["2015-04-01", 4], ["2015-04-05", 1]]},
# {:Kiwi=>["2015-04-05", 3]}]
我的方法中可以使用以下数据:
来自第一次服务调用的数据:
date: 2015-04-01 my_array = [{Apple: 3}, {Banana: 2}, {Oranges: 4}]
来自第二次服务调用的数据:
date: 2015-04-05 my_array = [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}]
在该方法的最后,我想 return 一个散列数组,其中包含从多个服务调用中收集的数据。
逻辑应该检查该键是否已存在于哈希中,如果是,则将值添加到现有键,如果不存在,则为该新键创建一个键值对象。对于这个例子,我在第一次服务调用后的哈希看起来像:
my_final_array = [{Apple: [2015-04-01, 3]}, {Banana: [2015-04-01, 2]}, {Oranges: [2015-04-01, 4]}]
然而,在我们从第二次服务调用中获取数据后,我希望我的最终数组为:
my_final_array = [{Apple: [[2015-04-01, 3], [2015-04-05, 4]]}, {Banana: [[2015-04-01, 2], [2015-04-05, 5]]}, {Oranges: [[2015-04-01, 4], [2015-04-05, 1]]}, {Kiwi: [2015-04-05, 3]}]
有没有一种简单的方法可以获得我所期望的?
我拥有的算法对数据进行两次迭代,即创建一个数组以从所有服务调用中收集数据,然后对数组进行迭代以按键分组。
这是我最初尝试解决它的方法:
dates_array.each do |week_date|
my_array = #Collect data returned by service for each week_date.
my_array.each do |sample_data|
sample_array << [date, sample_data.keys.first, sample_data.values.first]
end
end
sample_hash = sample_array.each_with_object({}) { |data_value, key_name|
(key_name[data_value[1]] ||= []) << data_value.values_at(0,2)
}
#Convert sample_hash to my_final_array for third party input.
当您有这些特定要求时,最好只创建您自己的 class - 这样您就可以在内部存储数据,但这是最好的。例如
class FunkyThing
def initialize
@s = {}
end
def add date, arr
arr.each do |e|
k, v = e.flatten
( @s[k] ||= [] ) << [ date, v ]
end
end
def val
@s.map { |k, v| { k => v } }
end
end
那么:
[142] pry(main)> a = FunkyThing.new
=> #<FunkyThing:0x007fbc23ed5cb0 @s={}>
[143] pry(main)> a.add '2015-04-01', [{Apple: 3}, {Banana: 2}, {Oranges: 4}]
=> [{:Apple=>3}, {:Banana=>2}, {:Oranges=>4}]
[144] pry(main)> a.val
=> [{:Apple=>[["2015-04-01", 3]]}, {:Banana=>[["2015-04-01", 2]]}, {:Oranges=>[["2015-04-01", 4]]}]
[145] pry(main)> a.add '2015-04-05', [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}]
=> [{:Apple=>4}, {:Banana=>5}, {:Oranges=>1}, {:Kiwi=>3}]
[146] pry(main)> a.val
=> [{:Apple=>[["2015-04-01", 3], ["2015-04-05", 4]]}, {:Banana=>[["2015-04-01", 2], ["2015-04-05", 5]]}, {:Oranges=>[["2015-04-01", 4], ["2015-04-05", 1]]}, {:Kiwi=>[["2015-04-05", 3]]}]
[147] pry(main)>
请注意,第一个输出与您在问题中要求的不同,因为这些值已经嵌套在第二层,我认为这可能是您想要的,所以我将其保留原样。
可能是这样的:
array_of_possible_keys.each do |key|
if my_final_hash.has_key?(key)
do something
else
do other thing
end
end
如果您改用散列,则不必遍历数组。而且我看不出有任何理由不更换
my_array = [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}]
my_final_array = [{Apple: [2015-04-01, 3]}, {Banana: [2015-04-01, 2]}, {Oranges: [2015-04-01, 4]}]
和
my_hash= {Apple: 4, Banana: 5, Oranges: 1, Kiwi: 3}
my_final_hash = {Apple: [2015-04-01, 3], Banana: [2015-04-01, 2], Oranges: [2015-04-01, 4]}
这是一个函数,它接受数组的当前版本、日期和要处理的新数组。
如果是第一次服务调用,则根据参数时间和要处理的数组创建一个新的数组。对于后续的服务调用,将根据数组的当前版本创建散列,然后处理参数(新)数组以将值添加到散列中。最后,哈希被转换回其原始数组形式。
请参考以下示例代码:
解决方案
def process_array(old_array: nil, date: date, my_array: my_array)
unless old_array
# service call # 1
my_array.each do |key_value_pair|
pair = key_value_pair.to_a.first
key = pair[0]
value = pair[1]
key_value_pair[key] = [date, value]
end
return my_array
else
# service call # 2 onwards
hash = {}
old_array.each do |key_value_pair|
pair = key_value_pair.to_a.first
key = pair[0]
value = pair[1]
hash[key] = value
end
my_array.each do |key_value_pair|
pair = key_value_pair.to_a.first
key = pair[0]
value = pair[1]
if hash.has_key?(key)
unless hash[key].first.kind_of?(Array)
hash[key] = [hash[key]]
end
hash[key] << [date, value]
else
hash[key] = [date, value]
end
end
output_array = []
hash.each do |key, value|
new_hash = {}
new_hash[key] = value
output_array << new_hash
end
output_array
end
end
用法
service_1 = [{Apple: 3}, {Banana: 2}, {Oranges: 4}]
array_1 = process_array(old_array: nil, date: "2015-04-01", my_array: service_1)
puts array_1.to_s
# => [{:Apple=>["2015-04-01", 3]}, {:Banana=>["2015-04-01", 2]}, {:Oranges=>["2015-04-01", 4]}]
service_2 = [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}]
array_2 = process_array(old_array: array_1, date: "2015-04-05", my_array: service_2)
puts array_2.to_s
# => [{:Apple=>[["2015-04-01", 3], ["2015-04-05", 4]]}, {:Banana=>[["2015-04-01", 2], ["2015-04-05", 5]]}, {:Oranges=>[["2015-04-01", 4], ["2015-04-05", 1]]}, {:Kiwi=>["2015-04-05", 3]}]
如果您像这样存储数据:
data1 = [{ date: "2015-04-01",
my_array: [{Apple: 3}, {Banana: 2}, {Oranges: 4}] },
{ date: "2015-04-05",
my_array: [{Apple: 4}, {Banana: 5}, {Oranges: 1}, {Kiwi: 3}] }]
考虑将其更改为:
data2 = data1.map { |g|
{ date: g[:date],
my_hash: Hash[g[:my_array].flat_map(&:to_a)] }
}
#=> [{:date=>"2015-04-01",
# :my_hash=>{:Apple=>3, :Banana=>2, :Oranges=>4}},
# {:date=>"2015-04-05",
# :my_hash=>{:Apple=>4, :Banana=>5, :Oranges=>1, :Kiwi=>3}}]
我不知道这是否更适合您的目的,但我希望您能看到它。然后你可以得到想要的分组如下:
result = data2.each_with_object({}) do |g,h|
g[:my_hash].each do |k,v|
h.update(k=>[g[:date],v]) do |_,o,n|
case o.first
when Array then o.concat(n)
else [o,n]
end
end
end
end
#=> {:Apple=> [["2015-04-01", 3], ["2015-04-05", 4]],
# :Banana=> [["2015-04-01", 2], ["2015-04-05", 5]],
# :Oranges=>[["2015-04-01", 4], ["2015-04-05", 1]],
# :Kiwi=> ["2015-04-05", 3]}
嗯,不,这不是您所要求的,但我希望您也看到它,如果您发现它是一种更有用的数据结构。将其转换为您所要求的内容很容易,我将在下面进行转换,但首先,我想解释一下有关上述计算的一些事情。
result
的计算采用 Hash#update(又名 merge!
)的形式,它使用块来确定要合并的两个哈希中存在的键的值。块变量是 k,o,n
,其中:
k
是公共密钥(我已将其更改为_
以表示它未在块中使用);o
(for "old")是h
中k
的值,正在构造的hash;和n
(表示“new”)是g
中k
的值,被合并的散列。
如果你想让上面的:Kiwi
的值变成[["2015-04-05", 3]]
(我认为这样在处理结果时会更容易),将update
简化为:
h.update(k=>[[g[:date],v]]) { |_,o,n| o+n }
要将 result
转换为您要求的形式:
result.map { |k,a| { k=>a } }
#=> [{:Apple=> [["2015-04-01", 3], ["2015-04-05", 4]]},
# {:Banana=> [["2015-04-01", 2], ["2015-04-05", 5]]},
# {:Oranges=>[["2015-04-01", 4], ["2015-04-05", 1]]},
# {:Kiwi=>["2015-04-05", 3]}]