A list is an abstract data type (ADT) that represents an ordered list of items. The list may be empty. If not empty, the list has a first item followed by a list containing the rest of the items. This is not a rigorous definition.
An enumerable is Ruby's implementation of list
.
We'll look at list processing techniques and the wide range of areas in which they are applicable.
By the end of this lesson, students should be able to:
- Use
any?
andall?
to testEnumerables
elements - Use
map
to create anArray
of transformedEnumerable
elements - Use
reduce
to "summarize" the elements in anEnumerable
- Process streams and strings as lists
Fork, clone, branch (lesson), npm install, and bundle install.
What are some types of lists?
What are some things we'd put on those lists?
We'll compare and contrast Ruby's Array with JavaScript's Array.
First let's compare the list of methods for Array in each language.
Ruby and JavaScript Arrays both have a map and reduce method. Let's explore using them to see the similarities and differences.
Note that Ruby, by convention, denotes a predicate method with a terminal ?
.
[1] pry(main)> fibs = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
> var fibs = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34];
In JavaScript, we use a function to control the behavior of Array methods. In Ruby, we use a block ( {|p|}
for one line blocks, do ... end
for multi-line blocks).
Alternating between JavaScript and Ruby, write a script that:
- calculates the sum of the first ten Fibonacci numbers
- calculates the product of the first ten Fibonacci numbers
- calculates the sum of the the odd Fibonacci numbers
- calculates the product of the even Fibonacci numbers (excluding zero).
The following table contains a mapping of some of the methods that potentially touch all the elements in an Array.
JavaScript | Ruby
every
| all?
filter
| select
forEach
| each
map
| map
reduce
| reduce
some
| any?
What do you notice? Where are any?
and reduce
?
Ruby's Enumerable module provides many list processing methods based on the method each
. Ruby's Array class includes the Enumerable module.
In Ruby, modules serve two purposes. The first is to create name spaces. The second is to supply common behavior to a class.
The Math
module hides mathematical functions inside the name-space Math
so that short and common names don't pollute the global name-space (e.g. Math::Pi
or Math.cos
).
The Comparable module provide common operators to a class that implements the <=>
(spaceship) operator. Let's look at lib/card.rb
.
How do you compare cards?
In your squads create an algorithm to determine which of two cards, if either, is "greater" than the other.
Adding the spaceship operator to Card
.
Let's simulate Enumerable methods using a deck of cards. In your squad, one of you will act as the method and another as the block. The third squad member will record the result.
Let's use the scripts bin/people-array.js
and bin/people-array.rb
to explore Array methods in both Ruby and JavaScript. The data in the objects we'll be processing comes from the comma separated values (CSV) file data/people.csv
The Person objects we'll test against have properties/methods that align with the headers in data/people.csv
plus the method age
Use the bin/people-array.*
scripts to
- Count all the people who are older than you (or just pick an age). Younger.
- Count all the people whose first name and last name start with the same letter.
- Calculate the average age of all the people.
Ruby's File includes Enumerable
so we can use all of those methods to process files a character or a line (the default) at a time.
Other enumerable classes related to working with files include IO, and Dir.
I used the Ruby Standard Library class CSV to load data for the bin/*-array.rb
scripts.
Using bin/read-file.rb
we'll read all the lines in a file and print them.
Let's modify that script to mimic the behavior of the wc
(word count) utility.
Now we'll read two files at the same time. This is a good time to look at Enumerator which is what gets returned when we call each
without a block.
We'll also need to look briefly at exception handling as Enumerator relies on this mechanism.
Let's explore the start of writing a card game in Ruby using lib/card.rb
and lib/deck.rb
.
Ruby's Range class provides a convenient way to express sequence of integers.
[1] pry(main)> 1..10
We'll build a new range class that increments by a provided value. The requirement to creating an Enumerable
is a correct implementation of the method each
.
Hash includes Enumerable so we can treat it as a list
We'll see how - String can be treated as a lists even though it doesn't include Enumerable. String accomplishes this using the methods that start each_
which return an Enumerator
.
Let's implement normalize_words
, unique_words
, word_count
, and word_frequencies
in lib/string.rb
Two images to give you a sense of the relationships in Ruby.
These images may diverge slightly from the actual relationships, Ruby is an evolving language, but do give a sense of much of what goes on.