创建哈希并检查密钥是否已存在

时间:2015-04-08 18:36:47

标签: ruby arrays hash

我的方法可以使用以下数据:

  • 来自首次服务电话的数据:

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

在方法结束时,我想返回一个哈希数组,它将从多个服务调用中收集数据。

逻辑应该检查密钥是否已经存在于散列中,如果是,则将值添加到现有密钥,如果不存在,则为该新密钥创建键值对象。对于此示例,第一次服务调用后的哈希值如下所示:

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.

4 个答案:

答案 0 :(得分:1)

当您有这些特定要求时,最好只创建自己的类 - 这样您就可以在内部存储数据,但这是最好的。例如

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)> 

请注意,第一个输出与您在问题中要求的输出不同,因为这些值已经嵌套在第二级,我想这可能是您想要的,所以我保持原样。

答案 1 :(得分:0)

像这样的东西,也许:

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

答案 2 :(得分:0)

这是一个接受当前版本的数组,日期和要处理的新数组的函数。

如果是第一次服务调用,则根据要处理的参数时间和数组创建一个新数组。对于后续服务调用,将根据数组的当前版本创建散列,然后处理参数(新)数组以向散列添加值。最后,哈希被转换回原始数组形式。

请参阅下面的示例代码:

<强>解决方案

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

答案 3 :(得分:0)

如果您要存储这样的数据:

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(对于“旧”)是kh的值,正在构造的哈希值;和
  • n(对于“new”)是kg的值,哈希被合并。

如果您希望上面: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]}]