DEV Community

Cover image for Enumerables Explained For a 5 year old
Brad Beggs
Brad Beggs

Posted on

Enumerables Explained For a 5 year old

Jump To: Find....Select|Find_all.... Map|Collect....Each....Definitions

You store lots of information in a list (aka as a collection, an array, a hash). In Ruby there are fairly common ways to ‘flip’ through each item in your collection so that you can do something with or to your collection items. As an aside, this flipping through is properly known as enumeration, hence Enumerables (check out this article's end for precise definitions).

When learning Ruby, it is important to understand what each Enumerable tool gives you back once the Enumerable finishes; this giving back is called the return value.

Why do we care about the return value and the return value's format?

Here is an IRL example: I want to make applesauce from my backyard apple tree. If I ask my apple peeler tool to cut apples, it won’t because it can’t do anything but peel an apple and return to me a peeled apple; because I didn't use the tool correctly I’ll be frustrated and yell “why don’t you cut apples!” This will happen in code too.

Below are a few of the most common tools of the Enumerable toolset with plain explanations of what they do and what they give back (aka the return value). I stay high level and don't explain the "how" since "how" is frequently written elsewhere.

Find

When Find is finished it gives back (returns) the first matching item; the matching item is not returned in a container (that is, the item is not returned in an array, a hash, etc). No change is made to your original list either.

When to use: use Find to locate the first match and when you want to do something with the match/return value and not do something to the return value.

Return value: the matching value by itself; if no matches, Ruby returns you NIL which means it found nothing to match. Find leaves your starting array container unchanged.

IRL example: if you want to find a 10oz Fuji apple, tell Find to search your box of apples. Once (and if) Find locates the first matching 10oz apple, Find returns the match without any wrapping around it like a box or bag (or an array or hash). You get just the plain apple, ready for use.

Code Example:

colors = ["orange", "gray", "white", "purple", "gold"]

volunteer_color = colors.find{|color| color == "orange"}

# return value is ["orange"] inside the volunteer_color
Enter fullscreen mode Exit fullscreen mode

Select | Find_all

Select | Find_all locates all requested matches in your container; they do exactly the same thing (per Ruby-doc.org, there is no performance difference and they are aliases).

Ruby keeps your matching return items neat as Select | Find_all returns everything in a new container (aka an array). There is no change to your original collection either. Both tools use true/false (aka boolean values) to determine a match. In the end, you have a new collection (of the matches and the original unchanged collection.

When to use: use Select | Find_all when you want to do something with the matching items later.

Return value: a new array container of all matching values, or NIL if no matches. Your starting container remains unchanged.

IRL example: You want all 10oz Fuji apples from the pile of fruit (apples, berries, melons). Select | Find_all checks each fruit item against the 10oz Fuji apple you provided. Any and all matches are found/selected and returned to you in a basket but magically, the pile of fruit still technically contains the matching apples too.

Code Example:

colors = ["orange", "gray", "white", "purple", "gold",]

school_colors = colors.find_all do |color|
 color.length == 6
end

# return value is ["orange", "purple"] inside the variable school_colors

# colors is still ["orange", "gray", "white", "purple", "gold",]
Enter fullscreen mode Exit fullscreen mode

Map | Collect

Map | Collect change, modify, and manipulate all items in your starting container and do exactly the same job, just under different names. They give back a new container (an array) of all the changed items values, yet leave the starting container the same.

When to use: when you need to do something to the items (aka modify your items to make new items).

Return value: a new array of new items as a result of the code block instructions**. Your starting container is unchanged.

IRL example: you have a bowl (an array) of apples and you want each apple cut into 4 squares and placed into a new bowl (a new array) for later. Magically, your starting bowl (the starting array) isn’t changed.

Code Example:

colors = ["orange", "gray", "white", "purple", "gold",]

school_chants = colors.map do |color|
    if color.length == 6 
      "Go Volunteers!"
    else
       "Go Pirates!"
    end
end

# return value is ["Go Volunteers!", "Go Pirates!", "Go Volunteers!", "Go Volunteers!", "Go Pirates!"]
# colors is still ["orange", "gray", "white", "purple", "gold",]
Enter fullscreen mode Exit fullscreen mode

Each

Technically the each tool is not an enumerable tool but is worth knowing what it gives back as the return value is very different.

When to use: when you need to do something with all the information in your collection, rather than to the information.

Return value: gives back your original and unchanged container.

IRL example: you want to display your fruit basket. Each displays every fruit piece exactly as instructed and once displayed, gives you the same fruit basket back.

Code example:

colors = ["orange", "gray", "white", "purple", "gold",]

school_chant = colors.each do |color|
    if color == "orange"   
        "Go Volunteers!"
    else
        "Go Pirates!"
    end
end

#return value is ["orange", "gray", "white", "purple", "gold",] placed inside school_chant and clearly not a chant! 
Enter fullscreen mode Exit fullscreen mode

Definitions

Code Block: where the comparison/or other code happens, sometimes inside some curly brackets { } or a do/end. The code in between { } or do/end is called the block requirements.

The double pipes | | represents a single element (or element pair like a key/value in a hash) from the collection. This element is used for comparison, a search, or a calculation.

Consider the | element | a temporary placeholder for each item; the code block only has access to the | element | and what is in the element changes on each ‘flip’ through the collection.

number = [100, 1124, 55, 66)
# a do/end format
numbers.map do |number|   #<---- |number| is the element
      number += 1         #<---- this area is the block
end

#a {} format
numbers.map{|number| number +=1}
Enter fullscreen mode Exit fullscreen mode

Collection: A collection of things, can be an array, a hash, a linked list, a mathematical set, … (this is a general concept implemented at various places in ruby)

Enumeration: A list of all elements in a collection. Sometimes a collection itself is an enumeration, sometimes not (this is also a general concept)

Enumerable: A module providing a huge functionality for traversing and manipulating collections (this is a ruby-specific thing)

Enumerator / Iterator: An object to traverse a collection (this is a general concept and implemented in ruby)

Module - a larger collection of related things. You’ll learn more important details later in your Ruby journey.

The first four are nicely written by Calvin in Ruby Forms

Top comments (0)