【问题标题】:Find hash in another array and substract in Ruby在另一个数组中查找哈希并在 Ruby 中减去
【发布时间】:2015-06-19 04:31:09
【问题描述】:

我有两个哈希数组,如下所示:

a = [ { car_id: 1, motor_id: 1, quantity: 5 },
      { car_id: 1, motor_id: 2, quantity: 6 },
      { car_id: 5, motor_id: 3, quantity: 3 } ]

b = [ { car_id: 1, motor_id: 1, quantity: 2 },
      { car_id: 1, motor_id: 2, quantity: 3 } ]

对于具有相同 car_idmotor_id 的那些哈希,我想从 a 中的哈希中减去 b 中每个哈希的数量。所以,我的预期结果是:

c = [ {car_id: 1, motor_id: 1, quantity: 3},
      {car_id: 1, motor_id: 2, quantity: 3},
      {car_id: 5, motor_id: 3, quantity: 3 } ]

在 Ruby 中执行此操作的好方法是什么?我的想法是遍历a,并为每个元素查找b 中是否有任何具有相同car_idmotor_id 的元素,如果是,则减去并继续。

【问题讨论】:

    标签: ruby


    【解决方案1】:

    我建议您首先为b 创建一个哈希bqty,对于b 的每个元素(哈希)g,将[g[:car_id], g[:motor_id]] 映射到g[:quantity]

    bqty = b.each_with_object({}) {|g,h| h[[g[:car_id], g[:motor_id]]] = g[:quantity]}
      #=> {[1, 1]=>2, [1, 2]=>3}
    

    接下来,将a 的每个元素(哈希)g 映射到所需的哈希。这是通过将g 合并到一个空的哈希h(或g.dup)中来完成的,然后,如果存在bqty 的元素和键key = [h[:car_id], h[:motor_id]],则从h[:quantity] 中减去bqty[key]。请注意,这会使 ab 保持不变。

    a.map do |g|
      {}.merge(g).tap do |h|
        key = [h[:car_id], h[:motor_id]]
        h[:quantity] -= bqty[key] if bqty.key?(key)
      end
    end
      #=> [{:car_id=>1, :motor_id=>1, :quantity=>3},
      #    {:car_id=>1, :motor_id=>2, :quantity=>3},
      #    {:car_id=>5, :motor_id=>3, :quantity=>3}]
    

    antepenultimate1 行的替代方案是:

        h[:quantity] -= bqty[key].to_i
    

    nil.to_i #=> 0.

    1.怎么能错过使用这样一个词的机会?

    【讨论】:

    • 您可以使用 Hash.new(0) 代替 to_i 业务。
    • 好点,@Jordan,虽然我更喜欢我所拥有的两种选择。
    • 不错!谢谢。请问您为什么要这样做而不是我建议的方法?性能?
    • 是的,只是性能。创建哈希比搜索 :car:motor 的匹配值的 b 元素要长一点,但您只需执行一次。
    【解决方案2】:

    这基本上是您的建议,除了对 car_id/motor_id 的显式检查,它使用散列代替。

    ids = ->(h){h.values_at(:car_id, :motor_id)} # A function for extracting the key values
    lookup = Hash[*a.flat_map{|h| [ids[h], h]}] # A map from ids -> hash w/ quantity
    b.each{|h| lookup[ids[h]][:quantity] -= h[:quantity]} # Subtract each b from an a (will error if the a doesn't exist)
    a == c # == true
    

    【讨论】:

    • 是的,我有点得意忘形了……不过这很有趣!
    • 这给出了错误的答案,因为没有b 的元素对应于a[2]。应该不难修复。
    • 我很抱歉。我不知何故错过了你正在变异a(即使你有a==c #=> true)。我正在查看来自b.each... 的返回,当然只是b
    • 是的...突变是最糟糕的。但另一种方法是对a 进行深度复制也很烦人。
    【解决方案3】:

    也许,您会发现以下更直观:

        matched_result = []
        while current_a = a.shift
          if current_b = b.shift
            if current_a[:car_id] == current_b[:car_id]  && current_a[:motor_id] == current_b[:motor_id]
              matched_result << {car_id: current_a[:car_id], motor_id: current_a[:motor_id], quantity: current_a[:quantity] - current_b[:quantity]}
            end
          else
            matched_result << current_a
          end
        end
        p matched_result
    

    【讨论】:

    • 这依赖于b 中键值对的顺序。如果我们反转它们 (b[0], b[1] = b[1], b[0]),则返回 [{:car_id=&gt;5, :motor_id=&gt;3, :quantity=&gt;3}]
    • @Cary 你说得对,这个解决方案依赖于 b 数组中的数据序列。如果您决定更改数据序列,您只需替换原始代码中的一个单词,使其与此更改兼容。在第 3 行...将 b.shift 更改为 b.pop。就是这样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-15
    相关资源
    最近更新 更多