Strategies for Looping Through Collections in Ruby (Part 2: Select/Reject)
Without further ado, I bring you the long-awaited second part of my three part series on looping through collections in ruby. As I wrote in my previous article here, this three-part series is meant to serve as a simple summary of three(ish) enumerator class methods that I often come across when dealing with and manipulating collections (mostly arrays, but sometimes hashes) in Ruby. The three methods are Map(or Collect), Select(conversely, Reject), and Reduce (or Inject). These enumerator methods seem to be the most widely used when manipulating collections, and are thus important in any fundamental understanding of Ruby. Broadly speaking, the Map function manipulates elements of a collection based on given block parameters; the Select function “filters” a collection based on block parameters, and returns a new array based on these filters; the Reduce method returns a new value that combines elements of a collection based on block parameters. And just to be clear, when I say “block parameters”, I mean this:
{|item| block }Where the "item" refers to each element of the collection, and the "block" is the what is actually "being done" to each element in the array
This post is about the Select and Reject methods. I will give some examples of how these methods can be used, but first, let’s start off with some arrays to manipulate:
Got it? Good. Let’s begin with some code snippets.
This is pretty straightforward stuff. Again,|x|
is block variable, and it represents each element in the array on the given “loop” through the array. |x|
can be anything the programmer wants to call it; I’m using |x|
for brevity’s sake, but again, I could replace |x|
with |supercalifragilisticexpialidocious|
, and still work the exact same way (although this would technically make the program run slightly slower). In my example, |x|
is equal to “1” on the first iteration, “5” on the second, etc. The block argument that is being passed to each element is x.even?
; thus, each iteration through the array will check whether or not x
is even (the even?
is a public instance method, and as is generally the case in OOP/Ruby programming, the ?
at the end of the method means that the method is returning a boolean value (true/false)). The resulting new(this is important! The original array array is NOT being altered!) array is [12]
. The second line, in which reject is used, performs an almost identical operation; here, the method returns a new array that includes all elements in the original array that aren’t divisible by three.
Let’s move on to another snippet that includes an example of Ruby Regular Expressions:
In Ruby, a Regular Expression “is a special sequence of characters that helps you match or find other strings or sets of strings using a specialized syntax held in a pattern.”(Tutorialspoint.com) The block argument here, x =~ /[abiou]/
, is an example of regular expression syntax. As you can probably guess, the new array that is returned from this method contains any element in the original array that matches any of the characters in[abiou]
; in this case, the characters are the strings “a” and “b”.
Select and Reject can also be used with hashes. As is clear from the above code snippet, the block elements and parameters are nearly identical to those used when manipulating arrays, with the addition of second block element passed in (hashes, of course, have both keys and values; thus, when looping through hashes, both must be included as block elements).
So how would we perform this same function without the use of the Select and Reject methods? One of the best ways to conceptually grasp Ruby methods is to “rewrite” them; in other words, it’s good practice to write a loop that accomplishes the same end result without using those built-in methods. Here are two examples (both written using slightly shorthand syntax);
Both of these functions will return [12]. The first function uses the single line shorthand of if then else end
, as opposed to its multi-line equivalent; the second function uses a ternary operator to accomplish the same task.
So that’s that! A Quick Note on Bang! Methods (b0rowed from previous post):
This was a brief introduction to the Select/Reject methods. As a final note, I will add this: the bang(!) “method” can be added to the end of map or collect (e.g. word_array.map!), but be wary! This bang-method is unique in that it actually changes the original array to the new one as specified in the block argument. In all these previous examples, a new “copy” of the array was created, leaving the original array unchanged. The bang! method will erase and update the original array. This can be a somewhat risky thing to do, especially if that array that is being manipulated with the bang! method is also being referenced elsewhere in a block of code. I would therefore say to use the bang! method sparingly and wisely.
See you next time for Reduce/Inject!