1

I have the following array of hashes as input:

ar = [{"Sales"=>"11", "CustID"=>"Cust04"},
      {"Sales"=>"44.9", "CustID"=>"Cust04"},
      {"Sales"=>"79.17", "CustID"=>"Cust06"},
      {"Sales"=>"73.84", "CustID"=>"Cust06"},
      {"Sales"=>"34.9", "CustID"=>"Cust06"},
      {"Sales"=>"29.6825", "CustID"=>"Cust06"},
      {"Sales"=>"2048.7", "CustID"=>"Cust06"},
      {"Sales"=>"15.24", "CustID"=>"Cust02"},
      {"Sales"=>"54.874", "CustID"=>"Cust04"},
      {"Sales"=>"12.79", "CustID"=>"Cust08"},
      {"Sales"=>"22.65", "CustID"=>"Cust08"},
      {"Sales"=>"63.26", "CustID"=>"Cust08"},
      {"Sales"=>"16.207", "CustID"=>"Cust08"},
      {"Sales"=>"782.2", "CustID"=>"Cust07"},
      {"Sales"=>"215.45", "CustID"=>"Cust07"},
      {"Sales"=>"781.23", "CustID"=>"Cust07"},
      {"Sales"=>"370.14", "CustID"=>"Cust07"},
      {"Sales"=>"1.7", "CustID"=>"Cust09"},
      {"Sales"=>"22.405", "CustID"=>"Cust09"}
]

I am looking for an output as below, based on the total of sales, with a rank:

ar_out # =>
["Customer" => "Cust04", "TotalSales" => "xxxx", "Rank" => "1"]
sawa
  • 165,429
  • 45
  • 277
  • 381
sambs
  • 13
  • 2
  • If you need to iterate over array—do it. – Aleksei Matiushkin Aug 31 '18 at 09:21
  • Ruby is an object-oriented language, not an array-of-hashes-of-strings-oriented language (unless, of course, your data is genuinely best expressed in this way!). Have you considered structuring this as e.g. an array of `Transaction` (or whatever each hash represents) objects? Your method could then return e.g. an array of `TransactionsSummary` objects. – Tom Lord Aug 31 '18 at 09:58
  • @TomLord with all due respect, array-of-hashes-of-strings is an object, full of objects each full of another objects :) – Aleksei Matiushkin Aug 31 '18 at 10:24
  • @mudasobwa I did say *"(unless, of course, your data is genuinely best expressed in this way!)"* – Tom Lord Aug 31 '18 at 10:48

2 Answers2

2

You can try this way

Input

ar = [
  { "Sales" => "11", "CustID" => "Cust04" },
  { "Sales" => "44.9", "CustID" => "Cust04" },
  { "Sales" => "79.17", "CustID" => "Cust06" },
  { "Sales" => "73.84", "CustID" => "Cust06" },
  { "Sales" => "34.9", "CustID" => "Cust06" },
  { "Sales" => "29.6825", "CustID" => "Cust06" },
  { "Sales" => "2048.7", "CustID" => "Cust06" },
  { "Sales" => "15.24", "CustID" => "Cust02" },
  { "Sales" => "54.874", "CustID" => "Cust04" },
  { "Sales" => "12.79", "CustID" => "Cust08" },
  { "Sales" => "22.65", "CustID" => "Cust08" },
  { "Sales" => "63.26", "CustID" => "Cust08" },
  { "Sales" => "16.207", "CustID" => "Cust08" },
  { "Sales" => "782.2", "CustID" => "Cust07" },
  { "Sales" => "215.45", "CustID" => "Cust07" },
  { "Sales" => "781.23", "CustID" => "Cust07" },
  { "Sales" => "370.14", "CustID" => "Cust07" },
  { "Sales" => "1.7", "CustID" => "Cust09" },
  { "Sales" => "22.405", "CustID" => "Cust09" }
]

Proces

ar.each_with_object(Hash.new(0)) { |hsh, e| e[hsh['CustID']] += hsh['Sales'].to_f }.
  sort_by { |_, v| -v }.
  map.with_index { |(k, v), i| [{ 'Customer' => k, 'TotalSales' => v, 'Rank' => i + 1 }] }

Output

[
  [
    {
      "Customer": "Cust06",
      "TotalSales": 2266.2925,
      "Rank": 1
    }
  ],
  [
    {
      "Customer": "Cust07",
      "TotalSales": 2149.02,
      "Rank": 2
    }
  ],
  [
    {
      "Customer": "Cust08",
      "TotalSales": 114.90699999999998,
      "Rank": 3
    }
  ],
  [
    {
      "Customer": "Cust04",
      "TotalSales": 110.774,
      "Rank": 4
    }
  ],
  [
    {
      "Customer": "Cust09",
      "TotalSales": 24.105,
      "Rank": 5
    }
  ],
  [
    {
      "Customer": "Cust02",
      "TotalSales": 15.24,
      "Rank": 6
    }
  ]
]
Lukas Baliak
  • 2,849
  • 2
  • 23
  • 26
  • Pretty clean. I suggest changing `.each_with_index.map` to `.map.with_index` the block can stay the same. – 3limin4t0r Aug 31 '18 at 11:31
  • 1
    Furthermore you might want to consider using `BigDecimal` since floats are not accurate. `1.03 - 0.2 #=> 0.8300000000000001` https://stackoverflow.com/q/3730019/3982562 – 3limin4t0r Aug 31 '18 at 11:36
1
ar.
  each_with_object({}) do |hash, acc|
    (acc[hash["CustID"]] ||= {"Customer" => hash["CustID"], "TotalSales" => 0}).
      tap { |h| h["TotalSales"] += hash["Sales"].to_f }
  end.
  values.
  sort_by { |h| -h["TotalSales"] }.
  map.
  with_index(1) { |h, idx| h.merge("Rank" => idx) }

#⇒ [{"Customer"=>"Cust06", "TotalSales"=>2266.2925, "Rank"=>1},
#   {"Customer"=>"Cust07", "TotalSales"=>2149.02, "Rank"=>2},
#   {"Customer"=>"Cust08", "TotalSales"=>114.90699999999998, "Rank"=>3},
#   {"Customer"=>"Cust04", "TotalSales"=>110.774, "Rank"=>4},
#   {"Customer"=>"Cust09", "TotalSales"=>24.105, "Rank"=>5},
#   {"Customer"=>"Cust02", "TotalSales"=>15.24, "Rank"=>6}]
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160