Preface
Goal: A practical case to collect unique record fields using Ruby.
After Python, why not Ruby?
Reference Reading
Most of the time, I just google things rather than, reading official documentation.
Source Examples
You can obtain source examples here:
Common Use Case
Task: Get the unique tag string
Please read overview for more detail.
Prepopulated Data
Songs and Poetry
module Songs
SONGS = [
{ 'title' => 'Cantaloupe Island',
'tags' => ['60s', 'jazz'] },
{ 'title' => 'Let It Be',
'tags' => ['60s', 'rock'] },
{ 'title' => 'Knockin\' on Heaven\'s Door',
'tags' => ['70s', 'rock'] },
{ 'title' => 'Emotion',
'tags' => ['70s', 'pop'] },
{ 'title' => 'The River' },
]
endRuby Solution
The Answer
There are different waty actually. One of them is this oneliner as below:
require_relative 'MySongs'
include Songs
tags = SONGS
.map { |song| song['tags'] }
.compact
.flatten
.uniq
puts "#{tags}"Enough with introduction, at this point we should go straight to coding.
Environment
No need any special setup. Just run and voila..!
1: Data Structure Using Hash
We are going to use array and hash,
throught out this article.
Simple Array
Consider begin with simple array.
tags = ["rock", "jazz", "rock", "pop", "pop"]
# expression substitution
puts "#{tags}"To dump array in ruby,
you should using expression substitution,
instead of just plain puts.
The result is similar as below list:
❯ ruby 01-tags.rb
["rock", "jazz", "rock", "pop", "pop"]
Hash
Writing record in hash is straightforward.
song = { 'title' => 'Cantaloupe Island',
'tags' => ['60s', 'jazz'] }
puts songWith the result as below:
❯ ruby 02-song.rb
{"title"=>"Cantaloupe Island", "tags"=>["60s", "jazz"]}
The Songs Structure
We can continue our journey to records just using hash.
No need any complex structure.
songs = [
{ 'title' => 'Cantaloupe Island',
'tags' => ['60s', 'jazz'] },
{ 'title' => 'Let It Be',
'tags' => ['60s', 'rock'] },
{ 'title' => 'Knockin\' on Heaven\'s Door',
'tags' => ['70s', 'rock'] },
{ 'title' => 'Emotion',
'tags' => ['70s', 'pop'] },
{ 'title' => 'The River' },
]
songs.each do |song| puts song endWith the result similar as below record:
❯ ruby 03-songs.rb
{"title"=>"Cantaloupe Island", "tags"=>["60s", "jazz"]}
{"title"=>"Let It Be", "tags"=>["60s", "rock"]}
{"title"=>"Knockin' on Heaven's Door", "tags"=>["70s", "rock"]}
{"title"=>"Emotion", "tags"=>["70s", "pop"]}
{"title"=>"The River"}2: Separating Module
Since we need to reuse the songs record multiple times, it is a good idea to separate the record structure from logic.
Songs Module
The code can be shown as below:
module Songs
SONGS = [
{ 'title' => 'Cantaloupe Island',
'tags' => ['60s', 'jazz'] },
{ 'title' => 'Let It Be',
'tags' => ['60s', 'rock'] },
{ 'title' => 'Knockin\' on Heaven\'s Door',
'tags' => ['70s', 'rock'] },
{ 'title' => 'Emotion',
'tags' => ['70s', 'pop'] },
{ 'title' => 'The River' },
]
end
Using Songs Module
Now we can have a very short code.
require_relative 'MySongs'
include Songs
SONGS.each do |song| puts song endWith the result exactly the same as above array of hash.

❯ ruby 04-module.rb
{"title"=>"Cantaloupe Island", "tags"=>["60s", "jazz"]}
{"title"=>"Let It Be", "tags"=>["60s", "rock"]}
{"title"=>"Knockin' on Heaven's Door", "tags"=>["70s", "rock"]}
{"title"=>"Emotion", "tags"=>["70s", "pop"]}
{"title"=>"The River"}3: Finishing The Task
Map, Compact, Flatten, Unique
There are at least three ways to do this.
- Filter Map
- Compact
- Select
All with the same result,
as list of list shown below.
❯ ruby 05-filter-map.rb
[["60s", "jazz"], ["60s", "rock"], ["70s", "rock"], ["70s", "pop"]]
❯ ruby 05-map-select.rb
[["60s", "jazz"], ["60s", "rock"], ["70s", "rock"], ["70s", "pop"]]
❯ ruby 05-compact.rb
[["60s", "jazz"], ["60s", "rock"], ["70s", "rock"], ["70s", "pop"]]However, we need to examine map first.
Map
Consider this map example below:
require_relative 'MySongs'
include Songs
tagss = SONGS.map { |song|
song['tags'] if song['tags'] != nil }
puts "#{tagss}"With the result of list of list, as shown below.
❯ ruby 05-comprehension.rb
[["60s", "jazz"], ["60s", "rock"], ["70s", "rock"], ["70s", "pop"], nil]Wait!
Why is there still nil?
Because, if the conditional false, it return nil.
It is the same as writing this:
tagss = SONGS.map { |song| song['tags'] }Now what is our option?
Filter Map
One combined function
tagss = SONGS.filter_map do |song|
song['tags'] if song['tags'] != nil
end Compact
Map, then compact
tagss = SONGS.map do |song|
song['tags']
end.compactSelect
Map, then select
tagss = SONGS
.map { |song| song['tags'] }
.select { |tags| tags != nil }
Unique
Built in flatten and uniq.
To solve unique list,
we can utilize built in method.
require_relative 'MySongs'
include Songs
tags = SONGS
.map { |song| song['tags'] }
.compact
.flatten
.uniq
puts "#{tags}"
The code is tidy.
What is Next 🤔?
We have alternative way to build the record structure.
Consider continue reading [ Ruby - Playing with Records - Part Two ].