简体   繁体   中英

How to aggregate ruby hash based on value of some keys

Given an array of hashes

arr = [{'city' => 'Bangalore','device' => 'desktop','users' => '20'},
       {'city' => 'Bangalore','device' => 'tablet','users' => '20'},
       {'city' => 'Bangalore','device' => 'mobile','users' => '20'},
       {'city' => 'Pune','device' => 'desktop','users' => '20'},
       {'city' => 'Pune','device' => 'tablet','users' => '20'},
       {'city' => 'Pune','device' => 'mobile','users' => '20'},
       {'city' => 'Mumbai','device' => 'desktop','users' => '20'},
       {'city' => 'Mumbai','device' => 'tablet','users' => '20'},
       {'city' => 'Mumbai','device' => 'mobile','users' => '20'}]    

How can I produce the following array of hashes?

[{'city' => 'Bangalore', 'users' => '60'},
 {'city' => 'Pune', 'users' => '60'},
 {'city' => 'Mumbai','users' => '60'}]   

Do it like :

hash = [{'city' => 'Bangalore','device' => 'desktop','users' => '20'}, {'city' => 'Bangalore','device' => 'tablet','users' => '20'}, {'city' => 'Bangalore','device' => 'mobile','users' => '20'}, {'city' => 'Pune','device' => 'desktop','users' => '20'},
 {'city' => 'Pune','device' => 'tablet','users' => '20'}, {'city' => 'Pune','device' => 'mobile','users' => '20'}, {'city' => 'Mumbai','device' => 'desktop','users' => '20'},
 {'city' => 'Pune','device' => 'tablet','users' => '20'}, {'city' => 'Mumbai','device' => 'mobile','users' => '20'}]   

new_hash = hash.map{|obj| {:city => obj[:city], :users => obj[:users]}} 

Try this

def setter hash, hash_array_new
  new_hash = {}
  new_hash["users"] = hash["users"].to_i
  new_hash["city"] = hash["city"]
  hash_array_new << new_hash
end

hash_array_new = []
hash_array.each do |hash|
  count = 0
  hash_array_new.each do |new_hash|
    if hash["city"] == new_hash["city"]
      new_hash["users"] += hash["users"].to_i
      count = count+1
    end
  end
  if count == 0
    setter hash, hash_array_new
  end
  if hash_array_new.blank?
    setter hash, hash_array_new
  end
end


puts hash_array_new //display resultant array
arr.each_with_object(Hash.new(0)) { |g,h| h[g["city"]] += g["users"].to_i }.
    map { |city,users| { 'city' => city, 'users' => users.to_s } }
  #=> [{"city"=>"Bangalore", "users"=>"60"},
  #    {"city"=>"Pune", "users"=>"60"},
  #    {"city"=>"Mumbai", "users"=>"60"}]  

Hash.new(0) is sometimes referred to as a counting hash. Zero is the default value. (See Hash::new .) This means that for

h[g["city"]] += g["users"].to_i

which expands to

h[g["city"]] = h[g["city"]] + g["users"].to_i

h[g["city"]] on the right side is replaced with the default value of zero when h does not have a key g["city"] .

Note that

arr.each_with_object(Hash.new(0)) { |g,h| h[g["city"]] += g["users"].to_i }
  #=> {"Bangalore"=>60, "Pune"=>60, "Mumbai"=>60}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM