Ruby 通过比较一个值进行散列合并

Ruby hash merge by comparing a value

我有以下哈希数组

[{:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>0},
 {:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>450},
 {:Day=>"May 2015", :Rent=>0, :Bond=>750, :RentInAdvance=>0},
 {:Day=>"Jun 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
 {:Day=>"Jul 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
 {:Day=>"Dec 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}]

我需要使用 Day 键值合并那些记录,预期结果是

[{:Day=>"May 2015", :Rent=>0, :Bond=>750, :RentInAdvance=>450},
 {:Day=>"Jun 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
 {:Day=>"Jul 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
 {:Day=>"Dec 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}]

提前致谢..

data = [{:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>0},
        {:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>450},
        {:Day=>"May 2015", :Rent=>0, :Bond=>750, :RentInAdvance=>0},
        {:Day=>"Jun 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
        {:Day=>"Jul 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
        {:Day=>"Dec 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}]

days = data.map{|d| d[ :Day ]}.uniq

def sum_by_day_for data, key
  data.map{| d| d[ key ]}.inject( :+ )
end

sum = days.map do |day|
  same_days = data.select{|d| d[ :Day ] == day}

  { Day:day, 
    Rent:sum_by_day_for( same_days, :Rent ), 
    Bond:sum_by_day_for( same_days, :Bond ),
    RentInAdvance:sum_by_day_for( same_days, :RentInAdvance )}
end

puts sum

TLDR:

data.group_by do |d|
  d[:Day]
end.values.map do |days|
  days.inject do |a,b|
    {
      Day: a[:Day],
      Rent: a[:Rent] + b[:Rent],
      Bond: a[:Bond] + b[:Bond],
      RentInAdvance: a[:RentInAdvance] + b[:RentInAdvance]
    }
  end
end

解释:

假设我们在 data 变量中有您的数据:

data = [{:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>0},
        {:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>450},
        {:Day=>"May 2015", :Rent=>0, :Bond=>750, :RentInAdvance=>0},
        {:Day=>"Jun 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
        {:Day=>"Jul 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
        {:Day=>"Dec 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}]

然后我们可以根据:Day值对数组进行分组(group_by),得到

grouped_data = data.group_by do |d|
  d[:Day]
end

# {"May 2015"=>
#   [{:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>0},
#    {:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>450},
#    {:Day=>"May 2015", :Rent=>0, :Bond=>750, :RentInAdvance=>0}],
#  "Jun 2015"=>[{:Day=>"Jun 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}],
#  "Jul 2015"=>[{:Day=>"Jul 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}],
#  "Dec 2015"=>[{:Day=>"Dec 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}]}

生成的散列的键是日期,值是数据散列数组。我们只对 values and map 数据哈希数组与单个合并数据哈希感兴趣:

merged_data = grouped_data.values.map do |days|
  days.inject do |a,b|
    {
      Day: a[:Day],
      Rent: a[:Rent] + b[:Rent],
      Bond: a[:Bond] + b[:Bond],
      RentInAdvance: a[:RentInAdvance] + b[:RentInAdvance]
    }
  end
end

inject 将两个数据哈希(ab)合并为一个新的哈希。 这给了我们最终的结果:

puts merged_data
# {:Day=>"May 2015", :Rent=>0, :Bond=>750, :RentInAdvance=>450}
# {:Day=>"Jun 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}
# {:Day=>"Jul 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}
# {:Day=>"Dec 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}

PS:如果您不熟悉这些方法,阅读有关这些方法的 ruby 文档可能会有所帮助。我为每个使用的方法添加了 link。

origin = [{:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>0},
   {:Day=>"May 2015", :Rent=>0, :Bond=>0, :RentInAdvance=>450},
    {:Day=>"May 2015", :Rent=>0, :Bond=>750, :RentInAdvance=>0},
     {:Day=>"Jun 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
      {:Day=>"Jul 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0},
       {:Day=>"Dec 2015", :Rent=>600, :Bond=>0, :RentInAdvance=>0}]

result = origin.reduce({}) do |memo, obj|
  day = obj[:Day]
  memo[day] ||= Hash.new(0)
  memo[day][:Rent] = [memo[day][:Rent], obj[:Rent]].max
  memo[day][:Bond] = [memo[day][:Bond], obj[:Bond]].max
  memo[day][:RentInAdvance] = [memo[day][:RentInAdvance], obj[:RentInAdvance]].max
  memo
end.reduce([]) do |memo, obj|
  memo << {:Day => obj[0]}.merge(obj[1])
end

p result

我不知道你将如何处理 Rent、Bond 和 RentInAdvance,所以我只找到最大值。

i = 0
while i < arr.length-1
    j = i+1
    while j < arr.length-1
        #Compare dates of hash with another_hash
        if arr[i][:Day] == arr[j][:Day]
            #If matches, add another_hash values(numerical) to corresponding keys of hash
            arr[i].merge!(arr[j]) {|key, vi, vj| vi.is_a?(Fixnum) ? (vi + vj) : vi }
            #Delete another_hash after its values has been added to hash
            arr.delete_at(j)
            #Next, compare at same index since it now points to next hash after its previous value(another_hash) has been deleted
            j -= 1
        end
        j += 1
    end
    i += 1
end
arr