【问题标题】:Array with hash, how to merge same keys and add its value带有哈希的数组,如何合并相同的键并添加它的值
【发布时间】:2016-05-03 11:50:33
【问题描述】:

我有一个包含哈希的数组。如果它们具有相同的键,我只想添加它的值。

@receivers << result

@receivers
=> [{:email=>"user_02@yorlook.com", :amount=>10.00}]
result
=> {:email=>"user_02@yorlook.com", :amount=>7.00}

我希望上面的结果看起来像这样

[{:email=>"user_02@yorlook.com", :amount=>17.00}]

有人知道怎么做吗?

这是整个方法

  def receivers
    @receivers = []
    orders.each do |order|
      product_email = order.product.user.paypal_email
      outfit_email  = order.outfit_user.paypal_email
      if order.user_owns_outfit?
        result = { email: product_email, amount: amount(order.total_price) }
      else
        result = { email: product_email, amount: amount(order.total_price, 0.9),
                   email: outfit_email,  amount: amount(order.total_price, 0.1) }
      end
      @receivers << result
    end
  end

【问题讨论】:

  • “相同的键”是指相同的:email 值?
  • @Stefan,这是个好问题..

标签: ruby


【解决方案1】:

使用Enumerable#group_by

@receivers.group_by {|h| h[:email]}.map do |k, v|
  {email: k, amount: v.inject(0){|s,h| s + h[:amount] } }
end
# => [{:email=>"user_02@yorlook.com", :amount=>17.0}]

使用Enumerable#each_with_object

@receivers.each_with_object(Hash.new(0)) {|h, nh| nh[h[:email]]+= h[:amount] }.map do |k, v|
 {email: k, amount: v}
end

【讨论】:

    【解决方案2】:
    # Output: [{ "em@il.one" => 29.0 }, { "em@il.two" => 39.0 }]
    def receivers
      return @receivers if @receivers
      # Produces: { "em@il.one" => 29.0, "em@il.two" => 39.0 }
      partial_result = orders.reduce Hash.new(0.00) do |result, order|
        product_email = order.product.user.paypal_email
        outfit_email  = order.outfit_user.paypal_email
    
        if order.user_owns_outfit?
          result[product_email] += amount(order.total_price)
        else
          result[product_email] += amount(order.total_price, .9)
          result[outfit_email]  += amount(order.total_price, .1)
        end
    
        result
      end
    
      @receivers = partial_result.reduce [] do |result, (email, amount)|
        result << { email => amount }
      end
    end
    

    【讨论】:

      【解决方案3】:

      我会这样写代码:

      def add(destination, source)
        if destination.nil?
          return nil
        end
        if source.class == Hash
          source = [source]
        end
        for item in source
          target = destination.find {|d| d[:email] == item[:email]}
          if target.nil?
            destination << item
          else
            target[:amount] += item[:amount]
          end
        end
        destination
      end
      

      用法:

      @receivers = []
      add(@receivers, {:email=>"user_02@yorlook.com", :amount=>10.00})
      => [{:email=>"user_02@yorlook.com", :amount=>10.0}]
      add(@receivers, @receivers)
      => [{:email=>"user_02@yorlook.com", :amount=>20.0}]
      

      【讨论】:

      • 显式类型检查和for 循环不是很惯用。
      • 好吧,它不在循环中,但想法是您可以提供不同的方法来调用该方法
      • 纯粹是风格,但请考虑return nil if destination.nil?; source = [source] if source_class == Hash。 (或if Hash === source)。您可以编写for item in [*source],而不是将source 转换为数组。 (这是我第一次写for... :-))。
      【解决方案4】:
      a = [
        {:email=>"user_02@yorlook.com", :amount=>10.0},
        {:email=>"user_02@yorlook.com", :amount=>7.0}
      ]
      
      a.group_by { |v| v.delete :email } # group by emails
       .map { |k, v| [k, v.inject(0) { |memo, a| memo + a[:amount] } ] } # sum amounts
       .map { |e| %i|email amount|.zip e } # zip to keys
       .map &:to_h # convert nested arrays to hashes
      

      【讨论】:

        【解决方案5】:

        据我了解,您可以只使用.inject

        a = [{:email=>"user_02@yorlook.com", :amount=>10.00}]
        b = {:email=>"user_02@yorlook.com", :amount=>7.00}
        c = {email: 'user_03@yorlook.com', amount: 10}
        
        [a, b, c].flatten.inject({}) do |a, e|
          a[e[:email]] ||= 0
          a[e[:email]] += e[:amount]
          a
        end
        
        => {
          "user_02@yorlook.com" => 17.0,
          "user_03@yorlook.com" => 10
        }
        

        【讨论】:

          猜你喜欢
          • 2018-02-12
          • 1970-01-01
          • 2020-11-16
          • 1970-01-01
          • 2013-02-24
          • 2018-06-10
          • 2016-06-30
          • 2016-05-29
          • 1970-01-01
          相关资源
          最近更新 更多