Category Archives: Groovy

Groovy and Ruby nested Hashes with default value, a short comparison

A bit of Groovy / Ruby comparison today! Recently I needed to write a Groovy script that needed to convert an array of objects to a structured and nested report. And I stumbled on Groovy’s default values for Hashes, described here. I loved how you can create nested hashes with default on all levels:

nested_hash = [:].withDefault() { [:].withDefault() {0} }

It felt so nice to remove all those checks (if a key exists … otherwise …) and it cleaned up the code nicely, making it more intent revealing. A big plus!

So, I wanted to see how Ruby would do. And gues what? It didn’t feel as natural at first sight.

For example, I expected this to work out of the box:

nested_hash = Hash.new(Hash.new(0))

But, very fishy things started to happen, making feel lost quite a bit. A correct  description of issues with this approach is summarised in this Stackoverflow question.

I guess this comes from the fact that Groovy treats non-existent hash key access with default value differently:

  • Groovy creates non-existent keys by default
  • Ruby doesn’t, it just returns the default value

The effect is such that with Groovy the default value is never changed, and you pile up items in your hash by accessing non-existent items.

For Ruby, a bit different approach is needed:

nested_hash = Hash.new { |hash, key| hash[key] = Hash.new(0) }

In effect, this makes Ruby nested hash behave as Groovy one: creating non-existent keys when accessed. At the above link, you can find a solution to have endlessly deep hash if you’re interested.

Finally, I decided to solve a small scenario in both languages. The idea was that while having a family, one needs to create a report stating age group / decade distribution by gender for it. Nested hashes / dictionaries with default values seemed ideal for it. Here are implementations in both languages (in no particular order):


class Person {
public name
public gender
public age
public Person(name, gender, age) {
this.name = name
this.gender = gender
this.age = age
}
def decade() {
return "${age – (age % 10)}+"
}
}
family = [
new Person("Sofia", "female", 1),
new Person("Aiko", "female", 1),
new Person("Vanja", "male", 37),
new Person("Branka", "female", 38),
new Person("Fuma", "female", 83),
new Person("Ranko", "male", 64),
new Person("Mira", "female", 64),
new Person("Tomo", "male", 67),
new Person("Zorica", "female", 66),
new Person("Greta", "female", 14),
new Person("Gael", "male", 21),
new Person("Vigo", "male", 11),
new Person("Mitchell", "male", 43),
new Person("Michel", "female", 22)
]
by_gender_and_decade = [:].withDefault() { [:].withDefault() {0} }
family.each { person ->
by_gender_and_decade[person.gender][person.decade()] += 1
}
def prettify(hash) {
return hash.collect { key, value -> "${key} / ${value}" }.join(", ")
}
println "Family members by gender and age group:"
println "female: ${prettify(by_gender_and_decade["female"])}"
println "male: ${prettify(by_gender_and_decade["male"])}"


Person = Struct.new(:name, :gender, :age) {
def decade
"#{age – (age % 10)}+"
end
}
family = [
Person.new("Sofia", :female, 1),
Person.new("Aiko", :female, 1),
Person.new("Vanja", :male, 37),
Person.new("Branka", :female, 38),
Person.new("Fuma", :female, 83),
Person.new("Ranko", :male, 64),
Person.new("Mira", :female, 64),
Person.new("Tomo", :male, 67),
Person.new("Zorica", :female, 66),
Person.new("Greta", :female, 14),
Person.new("Gael", :male, 21),
Person.new("Vigo", :male, 11),
Person.new("Mitchell", :male, 43),
Person.new("Michel", :female, 22)
]
by_gender_and_decade = Hash.new { |hash, key| hash[key] = Hash.new(0) }
family.each do |person|
by_gender_and_decade[person.gender][person.decade] += 1
end
def prettify(hash)
hash.collect { |key, value| "#{key} / #{value}" }.join(", ")
end
puts "Family members by gender and age group:"
puts "female: #{prettify(by_gender_and_decade[:female])}"
puts "male: #{prettify(by_gender_and_decade[:male])}"

I like how Ruby is less verbose, especially regarding Person class / struct. But I must admit I like Groovy default nested hash values more, it somehow feels more natural. Then again, maybe for this situation the automatic creation of non-existent keys is good, but somewhere else it might not be such a good idea, so having the option to choose is worthwhile. I guess one just needs to get used to the flavor of the language used and that’s it. The rest of the code is pretty much the same, no surprises there. And in case you were wondering, yes, some of the names are from my own family 🙂

Tagged ,