简体   繁体   English

Ruby on Rails - Ruby,如何从具有相同键的两个散列中添加值,而不覆盖这些值?

[英]Ruby on Rails - Ruby, How to add the values from two hashes with same key, without overwriting the values?

first of all thank you for helping me with my SQL question, at this point.首先感谢您在这一点上帮助我解决我的 SQL 问题。

Now I'm struggling with another thing, makes me think if I should quit being a programmer to be honest.现在我正在为另一件事而苦苦挣扎,这让我想说实话,我是否应该放弃成为一名程序员。

Anyway, my problem is : I have an array of hashes( and inside that hash another ) like this:无论如何,我的问题是:我有一个散列数组(在该散列中还有另一个),如下所示:

[
 {"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}}, 
 {"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}},
 {"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}},
 ...
]

What I'm trying to accomplish is that, if there are two values with the same key, ie "A1" add those values into one whole hash, without overwriting the old values and having the month as a key desired output:我想要完成的是,如果有两个值具有相同的键,即“A1”将这些值添加到一个完整的哈希中,而不覆盖旧值并将月份作为所需的关键输出:

[
 {"A1"=> { 1 => { :balance=> "0.0000", :price=>"9.0000"} },
         { 7 => { :balance => "34030.0000", :price => "34030.0000" } }},
  and so on... 
]

Is this posible?这可能吗?

Due the current format of the data you have, you'll need more than a couple of transformations.由于您拥有的数据的当前格式,您将需要进行多次转换。 Most of them based on transforming the values of the resulting hash, after grouping the hashes in the array by their only key:它们中的大多数基于转换结果散列的值,在按唯一键对数组中的散列进行分组之后:

data
  .group_by { |hash| hash.keys.first }                                     # (1)
  .transform_values { |value| value.flat_map(&:values) }                   # (2)
  .transform_values { |values| values.index_by { |value| value[:month] } } # (3)

The first transformation is to group the current object, holding an array of hashes, by its only hash key, hence the keys.first , resulting in:第一个转换是将当前对象分组,持有一个哈希数组,通过它唯一的哈希键,因此keys.first ,导致:

{
  "A1"=>[
    {"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}}, 
    {"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}}
  ],
  "A3"=>[{"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}]
}

The second, is to extract only the values from each hash, in the resulting hash, with arrays of hashes:第二种是仅从每个散列中提取值,在结果散列中,使用散列数组:

{
  "A1"=>[
    {:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}, 
    {:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}
  ],
  "A3"=>[{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}]
}

Then, it just lacks to transform the array of hashes, to simply an hash, whose keys are the value of month :然后,它只是不需要将散列数组转换为简单的散列,其键是month的值:

{
  "A1"=>{
    1.0=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}, 
    7.0=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}
  },
  "A3"=>{4.0=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}
}

@Sebastian's answer is excellent. @Sebastian 的回答非常好。 For variety, let's also consider an iterative approach.对于多样性,让我们也考虑一种迭代方法。 Not sure if it's more efficient or easier to understand, but it's always good to understand multiple perspectives.不确定它是否更有效或更容易理解,但理解多个观点总是好的。

Setting up the input data you gave us:设置您提供给我们的输入数据:

arr = [
 {"A1"=>{:month=>1.0, :balance=>"0.0000", :price=>"9.0000"}}, 
 {"A1"=>{:month=>7.0, :balance=>"34030.0000", :price=>"34030.0000"}},
 {"A3"=>{:month=>4.0, :balance=>"34030.0000", :price=>"34030.0000"}}]

We create a new empty hash for our results.我们为我们的结果创建一个新的空哈希。

new_hash = {}

And now iterating over the original data.现在迭代原始数据。 We're going to make some assumptions about the form of the data.我们将对数据的形式做一些假设。

# We know each thing is going to be a hash.
arr.each do |hsh|
    # Set up some convenient variables for keys and 
    # values we'll need later.
    key = hsh.keys.first
    value = hsh.values.first
    month = value[:month]
    
    # If the output hash doesn't yet have the key,
    # give it the key and assign an empty hash to it.
    new_hash[key] ||= {}
    # Assign the value to the hash, keyed to the current month.
    new_hash[key][month] = value
    # ... and get rid of the month key that's now redundant.
    new_hash[key][month].delete(:month)
end

And the result is:结果是:

{"A1"=>{1.0=>{:balance=>"0.0000", :price=>"9.0000"}, 
        7.0=>{:balance=>"34030.0000", :price=>"34030.0000"}}, 
 "A3"=>{4.0=>{:balance=>"34030.0000", :price=>"34030.0000"}}}

Arguably it would be more useful for the desired return value to be a hash:可以说,将所需的返回值设为散列会更有用:

h = {"A1"=>{1=>{:balance=>    "0.0000", :price=>    "9.0000"},
            7=>{:balance=>"34030.0000", :price=>"34030.0000"}},
     "A3"=>{4=>{:balance=>"34030.0000", :price=>"34030.0000"}}}

That way you could write, for example:这样你就可以写,例如:

require 'bigdecimal'

BigDecimal(h['A1'][7][:price]) 
  #=> 0.3403e5

See BigDecimal .请参阅BigDecimal BigDecimal is generally used in financial calculations because it avoids round-off errors. BigDecimal通常用于财务计算,因为它可以避免舍入错误。

This result can be obtained by changing the values of :month to integers in arr :可以通过将:month的值更改为arr整数来获得此结果:

arr = [
  {"A1"=>{:month=>1, :balance=>    "0.0000", :price=>    "9.0000"}}, 
  {"A1"=>{:month=>7, :balance=>"34030.0000", :price=>"34030.0000"}},
  {"A3"=>{:month=>4, :balance=>"34030.0000", :price=>"34030.0000"}}
]

and by computing:并通过计算:

h = arr.each_with_object({}) do |g,h|
  k,v = g.flatten
  (h[k] ||= {}).update(v[:month]=>v.reject { |k,_| k == :month })
end

See Hash#flatten , Hash#update (aka merge! ) and Hash#reject .请参阅Hash#flattenHash#update (又名merge! )和Hash#reject

One could alternatively write:也可以这样写:

h = arr.each_with_object(Hash.new { |h,k| h[k] = {} }) do |g,h|
  k,v = g.flatten
  h[k].update(v[:month]=>v.reject { |k,_| k == :month })
end

See the form of Hash::new that takes a block.请参阅采用块的Hash::new形式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM