2
votes

Regrouper par valeurs uniques tout en additionnant / ajoutant d'autres valeurs

J'ai une structure de données qui ressemble à ceci:

[
  {
    price: 4.0,
    unit: "meter",
    tariff_code: "4901.99",
    amount: 400
   },
   {
    price: 2.0,
    unit: "yards",
    tariff_code: "6006.24",
    amount: 1000
   }
]

Je dois regrouper tous ces éléments par code_ tarifaire, tout en additionnant le prix et les montants correspondant à ce code tarifaire. Donc, ma sortie attendue devrait être:

arr = [
  {
    price: 2.0,
    unit: "meter",
    tariff_code: "4901.99",
    amount: 200
   },
   {
    price: 2.0,
    unit: "meter",
    tariff_code: "4901.99",
    amount: 200
   },
   {
    price: 14.0,
    unit: "yards",
    tariff_code: "6006.24",
    amount: 500
   },
   {
    price: 14.0,
    unit: "yards",
    tariff_code: "6006.24",
    amount: 500
  }
]

reception_data [: order_items] .group_by {| oi | oi [: code_ tarif]} .values ​​

L'instruction group_by utilisée ci-dessus me permettra de grouper par code_ tarifaire mais je ne parviens pas à trouver un moyen de faire la somme les autres valeurs. Je suis sûr qu'il existe un moyen simple d'accomplir cela ...


3 commentaires

Il me semble que vous devez utiliser un itérateur comme select , qui filtre sur des valeurs uniques de tarrif_code . Si vous essayez avec chaque élément d'un tableau, les méthodes Array sont ce que vous pouvez effectuer sur ce tableau. ruby-doc.org/core-2.4.1/Array.html


J'ai modifié votre tableau car il contenait quelques petites erreurs ( montant: était : montant: à deux endroits). J'ai également attribué une variable arr au tableau. Cela permet aux lecteurs de se référer à la variable dans les réponses et les commentaires sans avoir à la définir.


Merci pour ça! Désolé, j'étais pressé :)


4 Réponses :



2
votes

Plus verbeux:

grouped_items = arr.group_by { |oi| oi[:tariff_code] }
result = grouped_items.map do |tariff_code, code_items|
  price, amount = code_items.reduce([0, 0]) do |(price, amount), ci|
    [price + ci[:price], amount + ci[:amount]]
  end
  {
    price:       price,
    unit:        code_items.first[:unit],
    tariff_code: tariff_code,
    amount:      amount
  }
end
#[
#  {:price=>4.0, :unit=>"meter", :tariff_code=>"4901.99", :amount=>400}
#  {:price=>28.0, :unit=>"yards", :tariff_code=>"6006.24", :amount=>1000}
#]


0 commentaires

1
votes

Une approche simple, mais il est facile d'ajouter de nouvelles clés pour la sommation et de changer une clé de groupe. Je ne suis pas sûr de l'efficacité, mais 500_000 fois le benchmark de arr.map semble bon ici

summ_keys = %i[price amount]
grouping_key = :tariff_code
result = Hash.new { |h, k| h[k] = {} }
arr.map do |h|
  cumulative = result[h[grouping_key]]
  h.each do |k, v|
    case k
    when *summ_keys
      cumulative[k] = (cumulative[k] || 0) + h[k]
    else
      cumulative[k] = v
    end
  end
end
p result.values

# [{:price=>4.0, :unit=>"meter", :tariff_code=>"4901.99", :amount=>400},
#  {:price=>28.0, :unit=>"yards", :tariff_code=>"6006.24", :amount=>1000}]
#<Benchmark::Tms:0x00007fad0911b418 @label="", @real=1.480799000000843, @cstime=0.0, @cutime=0.0, @stime=0.0017340000000000133, @utime=1.4783359999999999, @total=1.48007>


0 commentaires

2
votes

Juste pour ajouter à l'amusement, la réponse qui utilise group_by comme l'a dit @cary, et copie principalement la réponse de Pavel. Ceci est très mauvais en termes de performances et à n'utiliser que si le tableau est petit . Il utilise également sum qui n'est disponible que dans Rails. (peut être remplacé par .map {| item | item [: price]} .reduce (: +) en pur rubis)

arr.group_by(&:tariff_code).map do |tariff_code, items|
  {
    price: items.sum(&:price]),
    unit: items.first[:unit],
    tariff_code: tariff_code,
    amount: items.sum(&:amount)
  }
end

Cela aurait été pair plus petit s'il s'agissait d'un tableau d'objets (peut-être des objets ActiveRecord) avec des méthodes au lieu de hachages.

arr.group_by { |a| a[:tariff_code] }.map do |tariff_code, items|
  {
    price: items.sum { |item| item[:price] },
    unit: items.first[:unit],
    tariff_code: tariff_code,
    amount: items.sum { |item| item[:amount] }
  }
end


1 commentaires

Agréable et propre!