且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

红宝石 - 如何用条件max多个键来检索​​阵列组总和

更新时间:2021-12-09 02:39:47

这样做的一种方法是使用哈希#更新(又名合并!),使用块来确定的值这是两个散列值present键被合并。

One way of doing this is to use the form of Hash#update (aka merge!) that uses a block to determine the values of keys that are present in both hashes being merged.

code

def f_addition(arr, group_fields, sum_fields, max_fields)
  arr.each_with_object({}) do |h,g|
    g.update( h.values_at(*group_fields) => h ) do |_,gv,hv|
      gv.merge(hv) do |k,gvv,hvv|
        case
        when sum_fields.include?(k) then "%.2f" % (gvv.to_f + hvv.to_f)
        when max_fields.include?(k) then [gvv, hvv].max
        else gvv
        end
      end
    end
  end.values
end

示例

arr = [{ "id"=>2, "idx"=>111, "money"=>"4.00", "money1"=>"1.00",
         "order"=>"001", "order1"=>"1", "pet"=>"dog" },
       { "id"=>1, "idx"=>112, "money"=>"2.00", "money1"=>"2.00",
         "order"=>"001", "order1"=>"1", "sport"=>"darts" },
       { "id"=>3, "idx"=>113, "money"=>"3.00", "money1"=>"1.00",
         "order"=>"002", "order1"=>"2" }]

请注意,此数组是从问题中给出的略有不同。我已经加入宠儿=>中的狗来的第一个(散)元素运动=>中镖和第二哈希值。

Notice that the this array is slightly different from from the one given in the question. I have added "pet"=>"dog" to the first (hash) element "sport"=>"darts"and to the second hash.

f_addition(arr, ["order","order1"], ["money","money1"], ["id", "idx"] )
  #=> [{ "id"=>2, "idx"=>112, "money"=>"6.00", "money1"=>"3.00",
  #      "order"=>"001", "order1"=>"1", "pet"=>"dog", "sport"=>"darts"},
  #    { "id"=>3, "idx"=>113, "money"=>"3.00", "money1"=>"1.00",
  #      "order"=>"002", "order1"=>"2" }] 

说明

有关上面的例子:

group_fields = ["order", "order1"]
sum_fields   = ["money", "money1"]
max_fields   = ["id", "idx"]

enum = arr.each_with_object({})
  #=> #<Enumerator: [{"id"=>2, "idx"=>111,..., "pet"=>"dog"},
  #     {"id"=>1, "idx"=>112,..., "sport"=>"darts"},
  #     {"id"=>3,"idx"=>113,...,"order1"=>"2"}]:each_with_object({})> 

阵列#每个每个传递此枚举成块元素,并将其分配给块变量。传递的第一个元素是:

Array#each passes each element of this enumerator into the block and assigns it to the block variables. The first element passed is:

h, g = enum.next
  #=> [{ "id"=>2, "idx"=>111, "money"=>"4.00", "money1"=>"1.00",
         "order"=>"001", "order1"=>"1", "pet"=>"dog" }, {}]  
h #=>  { "id"=>2, "idx"=>111, "money"=>"4.00", "money1"=>"1.00",
         "order"=>"001", "order1"=>"1", "pet"=>"dog" } 
g #=>  {} 

为:

h.values_at(*group_fields)
  #=> h.values_at(*["order", "order1"])
  #=> h.values_at("order", "order1")
  #=> ["001", "1"]

我们计算:

g.update(["001", "1"] => h) do |k,gv,hv| ... end

这是简写:

g.update({ ["001", "1"] => h }) do |k,gv,hv| ... end

做| K,GV,HV | ...结束只用时要合并的两个散列都包含键 K 1 G = {} 不包含任何键,该块不会在这个时候使用的:

The block do |k,gv,hv| ... end is only used when the two hashes being merged both contain the key k.1 As g = {} contains no keys, the block is not used at this time:

g.update({ ["001", "1"] => h })
  #=> {}.update({ ["001", "1"]=>{ "id"=>2, "idx"=>111, "money"=>"4.00",
  #                               "money1"=>"1.00", "order"=>"001",
  #                               "order1"=>"1", "pet"=>"dog" } }
  #=> { ["001", "1"]=>{ "id"=>2, "idx"=>111, "money"=>"4.00", "money1"=>"1.00",
  #                     "order"=>"001", "order1"=>"1", "pet"=>"dog" } } 

在由更新返回的值是新值先按g

枚举传递到块的下一个值是:

The next value of enum passed into the block is:

h, g = enum.next
h #=> { "id"=>1, "idx"=>112, "money"=>"2.00", "money1"=>"2.00",
  #     "order"=>"001", "order1"=>"1", "sport"=>"darts" },
g #=> { ["001", "1"]=>{ "id"=>2, "idx"=>111, "money"=>"4.00", "money1"=>"1.00",
  #      "order"=>"001", "order1"=>"1", "pet"=>"dog" } }] 

为:

h.values_at(*group_fields)
  #=> h.values_at("order", "order1")
  #=> ["001", "1"]

我们计算:

g.update(["001", "1"] => h) do |k,gv,hv| ... end

由于先按g {[001,1] =&GT; h}可以都包含键[001,1],我们必须按照块,以确定在合并后的哈希键的值。我们有:

As g and { ["001", "1"] => h } both contain the key ["001", "1"], we must defer to the block to determine the value of that key in the merged hash. We have:

k  = ["001", "1"]
gv = { "id"=>2, "idx"=>111, "money"=>"4.00", "money1"=>"1.00",
       "order"=>"001", "order1"=>"1", "pet"=>"dog" }
hv = { "id"=>1, "idx"=>112, "money"=>"2.00", "money1"=>"2.00",
       "order"=>"001", "order1"=>"1", "sport"=>"darts" }

我们因此如下评价块(使用合并,而不是合并/更新!):

We therefore evaluate the block as follows (using merge rather than merge!/update):

gv.merge(hv) do |k,gvv,hvv|
  case
  when sum_fields.include?(k) then "%.2f" % (gvv.to_f + hvv.to_f)
  when max_fields.include?(k) then [gvv, hvv].max
  else gvv
  end
end
  #=> { "id"=>2, "idx"=>112, "money"=>"6.00", "money1"=>"3.00",
  #     "order"=>"001", "order1"=>"1", "pet"=>"dog", "sport"=>"darts"}

GV 不包含关键的运动,因此合并运动=&GT当不使用块,飞镖 GV HVV 的所有其他键都在 GVV present ,不过,所以我们使用的块,以确定它们的值在合并后的哈希值。适用于:

gv does not contain the key "sport", so the block is not used when merging "sport"=>"darts" into gv. All other keys of hvv are present in gvv, however, so we use the block to determine their values in the merged hash. For:

k = "money"
gvv = "4.00"
hvv = "2.00"

我们发现:

sum_fields.include?(k)
  #=> ["money", "money1"].include?("money")
  #=> true

所以情况语句返回:

"%.2f" % (gvv.to_f + hvv.to_f)
  #=> "%.2f" % ("4.00".to_f + "2.00".to_f)
  #=> "6.00"

高压的其他键的值,散列合并到 GV ,也同样计算,给予我们合并后的散列的新值先按g

The values for other keys of hv, the hash being merged into gv, are computed similarly, to give us a new value for the merged hash g.

最后,

{ ["002", "order1"] => { "id"=>3, "idx"=>113, "money"=>"3.00",
                         "money1"=>"1.00", "order"=>"002", "order1"=>"2" }]

合并到先按g (不要求使用的块更新)和 g.values​​ 是由该方法返回。

is merged into g (which does not require the use update's block) and g.values is returned by the method.

观察

这将是易于推广这种通过对,如:

It would be easy to generalize this to pass pairs such as:

[["money","money1"], ->(a,b) { "%.2f" % (a.to_f + b.to_f) }]
[["id", "idx"], :max]

这可以做如下:

def f_addition(arr, group_fields, *mods)
  arr.each_with_object({}) do |h,g|
    g.update( h.values_at(*group_fields) => h ) do |_,gv,hv|
      gv.merge(hv) do |k,gvv,hvv|
        f,op = mods.find { |f,op| f.include?(k) }
        if f
          case op
          when Proc   then op.call(gvv,hvv)
          when Symbol then [gvv, hvv].send(op)
          end
        else
          gvv
        end
      end
    end
  end.values
end

f_addition(arr, ["order","order1"],
                [["money","money1"], ->(a,b) { "%.2f" % (a.to_f + b.to_f) }],
                [["id", "idx"], :max])
  # => [{ "id"=>2, "idx"=>112, "money"=>"6.00", "money1"=>"3.00",
  #       "order"=>"001", "order1"=>"1", "pet"=>"dog", "sport"=>"darts" },
  #      { "id"=>3, "idx"=>113, "money"=>"3.00", "money1"=>"1.00",
  #        "order"=>"002", "order1"=>"2" }]

1. We will find that the calculations in the block do not depend on the block variable `k`.
   I've therefore replaced that variable with the local variable _, to so-inform the reader.