【问题标题】:How to merge 2 arrays where value in one matches a value in another with different key in Ruby如何合并2个数组,其中一个值与另一个值与Ruby中的不同键匹配
【发布时间】:2021-01-23 20:12:13
【问题描述】:

我有一个数组,其中包含其他带有价格的商品数组,但是当一个商品进行销售时,会创建一个新商品我如何合并或将值从一个到另一个以创建 1 个数组,以便销售价格替换非出售但包含原价?

例子:

items=[{"id": 123, "price": 100, "sale": false},{"id":456,"price":25,"sale":false},{"id":678, "price":75, "sale":true, "parent_price_id":123}]

转化为:

items=[{"id":456,"price":25,"sale":false},{"id":678, "price":75, "sale":true, "parent_price_id":123, "original_price": 100}]

【问题讨论】:

  • 什么是original_price
  • 你确定items=[["id": 123, "price": 100, "sale": false],["id":456,"price":25,"sale":false],["id":678, "price":75, "sale":true, "parent_price":123]] 不应该是items=[{"id": 123, "price": 100, "sale": false},{"id":456,"price":25,"sale":false},{"id":678, "price":75, "sale":true, "parent_price":123}]] 吗?
  • 你是对的。我编辑了这个问题。原价来自与parent_price_id匹配的商品价格
  • 你尝试解决了吗?

标签: arrays ruby object merge


【解决方案1】:

让我们以等效但更熟悉的方式定义items

items = [
  [{:id=>123, :price=>100, :sale=>false}],
  [{:id=>456, :price=>25,  :sale=>false}],
  [{:id=>678, :price=>75,  :sale=>true, :parent_price=>123}]
] 

所需的返回值为:

[
  {:id=>456, :price=>25, :sale=>false},
  {:id=>678, :price=>75, :sale=>true, :parent_price=>123,
   :original_price=>100}
]

我假设h[:sale] #=> false 对应items(哈希)g 的每个元素g[:parent_price] = h[:id]

方便的第一步是创建以下哈希。

h = items.map { |(h)| [h[:id], h] }.to_h
  #=> {123=>{:id=>123, :price=>100, :sale=>false},
  #    456=>{:id=>456, :price=>25, :sale=>false},
  #    678=>{:id=>678, :price=>75, :sale=>true, :parent_price=>123}}

然后:

  h.keys.each { |k| h[k][:original_price] =
    h.delete(h[k][:parent_price])[:price] if h[k][:sale] }
    #=> [123, 456, 678] (not used)

  h #=> {456=>{:id=>456, :price=>25, :sale=>false},
    #    678=>{:id=>678, :price=>75, :sale=>true, :parent_price=>123,
    #          :original_price=>100}} 

注意Hash#delete 返回已删除键的值。

最后两个步骤是从此哈希中提取值并将items 替换为生成的哈希数组:

items.replace(h.values)
    #=> [{:id=>456, :price=>25, :sale=>false},
    #    {:id=>678, :price=>75, :sale=>true, :parent_price=>123,
    #     :original_price=>100}] 

Array#replace

如果需要,我们可以将这些步骤组合如下。

items.replace(
  items.map { |(h)| [h[:id], h] }.to_h.tap do |h|
    h.keys.each { |k| h[k][:original_price] =
      h.delete(h[k][:parent_price])[:price] if h[k][:sale] }
    end.values)
  #=> [{:id=>456, :price=>25, :sale=>false},
  #    {:id=>678, :price=>75, :sale=>true, :parent_price=>123,
  #     :original_price=>100}] 

Object#tap

【讨论】:

    【解决方案2】:

    这不是最漂亮的解决方案,但这是您可以做到的一种方法。我添加了一个 minitest 规范,以根据您提供的值检查它,它给出了您希望的答案。

    require "minitest/autorun"
    
    def merge_prices(prices)
      # Create a hash that maps the ID to the values
      price_map =
        prices
        .map do |price|
          [price[:id], price]
        end
        .to_h
      # Create a result array which is initially duplicated from the original
      result = prices.dup
      result.each do |price|
        if price.key?(:parent_price)
          price[:original_price] = price_map[price[:parent_price]][:price]
          # Delete the original
          result.delete_if { |x| x[:id] == price[:parent_price] }
        end
      end
      result
    end
    
    describe "Merge prices" do
      it "should work" do
        input = [
          {"id":123, "price": 100, "sale": false},
          {"id":456,"price":25,"sale": false},
          {"id":678, "price":75, "sale": true, "parent_price":123}
        ].freeze
        expected_output = [
          {"id":456,"price":25,"sale": false},
          {"id":678, "price":75, "sale": true, "parent_price":123, "original_price": 100}
        ].freeze
        assert_equal(merge_prices(input), expected_output)
      end
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多