Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building Hash by grouping array of objects based on a property of the items

Tags:

arrays

ruby

hash

I'm wondering if there is a more canonical way to do this in ruby 1.9

I have an array with a bunch of objects and I want to group them into a Hash using a property of each object in the array.

Very simplified example:

> sh = {}
 => {} 
> aers = %w(a b c d ab bc de abc)
 => ["a", "b", "c", "d", "ab", "bc", "de", "abc"] 
> aers.each do |aer|
>     sh[aer.size] = [] if sh[aer.size].nil?
>     sh[aer.size] << aer
>   end
=> ["a", "b", "c", "d", "ab", "bc", "de", "abc"] 
> sh
 => {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]} 

I tried this, but its output is wrong (as you can see):

 sh = Hash.new([])
 => {} 
> aers.each do |aer|
>     sh[aer.size] << aer
>   end
 => ["a", "b", "c", "d", "ab", "bc", "de", "abc"] 
> sh
 => {} 
like image 869
user141146 Avatar asked Jan 14 '11 15:01

user141146


People also ask

How do I group an array of objects in JavaScript?

Use reduce() to Group an Array of Objects in JavaScript The most efficient way to group an array of objects in JavaScript is to use the reduce() function. This method executes each array element's (specified) reducer function and produces a single output value.

How do you group items in an array?

The most efficient method to group by a key on an array of objects in js is to use the reduce function. The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.

Can an array have properties?

Arrays also have built-in properties, such as array. length . The length property carries an integer value that denotes the length of an array. In general, built-in properties can be frequently found in predefined JavaScript objects like arrays.

Can a property of an object be an array?

Just as object properties can store values of any primitive data type (as well as an array or another object), so too can arrays consist of strings, numbers, booleans, objects, or even other arrays.


3 Answers

Ruby has anticipated your need, and has got you covered with Enumerable#group_by:

irb(main):001:0> aers = %w(a b c d ab bc de abc)
#=> ["a", "b", "c", "d", "ab", "bc", "de", "abc"]

irb(main):002:0> aers.group_by{ |s| s.size }
#=> {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]}

In Ruby 1.9, you can make this even shorter with:

irb(main):003:0> aers.group_by(&:size)
#=> {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]}
like image 118
Phrogz Avatar answered Oct 25 '22 01:10

Phrogz


Phrogz is correct, group_by is there for the taking. Your code contains one of ruby's gotcha's.

aers = %w(a b c d ab bc de abc)
sh = Hash.new([]) # returns the _same_ array everytime the key is not found. 
# sh = Hash.new{|h,v| h[v] = []}  # This one works
p sh, sh.default

aers.each do |aer|
  sh[aer.size] << aer  #modifies the default [] every time
end
p sh, sh.default
p sh[5]

Output

{}
[]
{}
["a", "b", "c", "d", "ab", "bc", "de", "abc"]
["a", "b", "c", "d", "ab", "bc", "de", "abc"]
like image 22
steenslag Avatar answered Oct 25 '22 02:10

steenslag


You can also accomplish this by chaining methods... which might only be of academic interest for this problem, but is still a good technique to be familiar with.

irb(main):017:0> sh = {}
=> {}
irb(main):018:0> aers.collect{|k| k.size}.uniq!.each{|k| sh[k] = aers.select{|j| j.size == k}}
=> [1, 2, 3]
irb(main):019:0> sh
=> {1=>["a", "b", "c", "d"], 2=>["ab", "bc", "de"], 3=>["abc"]}
irb(main):020:0> 
like image 30
philosodad Avatar answered Oct 25 '22 02:10

philosodad