7 Interesting Ruby Array Methods
Arrays are one of the fundamental structures of programming. Being able to quickly manipulate and read data out of them is vital to success in building stuff with computers. Here are seven methods you can’t do without:
Map/Each
These two methods are very similar. They allow you to step through “each” item in an array and do something to it.
Check out some code:
1
2
3
array = [1, 2, 3]
effects = array.each{|x| # create record from x }
added = array.map{ |x| x + 2 }
If we read from added
, we’ll get [3, 4, 5]. If we read from effects
, we’ll still get [1, 2, 3]. Here’s the difference between
these two: .map
will return a new modified array, whereas .each
will return the original array.
Ruby’s .map
might seem a little strange. To give off a clearer view look at this example.7
1
2
3
4
5
6
7
8
9
# we create an array of records
array = [e, e2, e3]
# => [#<Event id: 1, name: nil>, #<Event id: 2, name: nil">, #<Event id: 3, name: nil>]
# so far so good
new_array = array.map{|e| e.name = "a name"; e}
# => [#<Event id: 1, name: "a name">, #<Event id: 2, name: "a name">, #<Event id: 3, name: "a name">]
# uh-oh, that ain't right
array
# => [#<Event id: 1, name: "a name">, #<Event id: 2, name: "a name">, #<Event id: 3, name: "a name">]
We might expect that we are working with some kind of copy of our records in the array, but we are not. That is all just to say:
be careful. You can easily create side effects in your .map
functions.
Select
.select
allows you to “find” an element in an array. You have to give .select
a function that returns true or false, 7
so it knows whether or not to “keep” an array element.
1
2
3
array = ['hello', 'hi', 'goodbye']
array.select{|word| word.length > 3}
# => ["hello", "goodbye"]
A slightly more complex example, probably getting closer to how you’d actually use this. Let’s throw in .map
at the end for
good measure:
1
2
3
4
valid_colors = ['red', 'green', 'blue']
cars = [{type: 'porsche', color: 'red'}, {type: 'mustang', color: 'orange'}, {type: 'prius', color: 'blue'}]
cars.select{ |car| valid_colors.include?(car[:color]) }.map{ |car| car[:type]}
# => ["porsche", "prius"]
Yes, you can indeed join these methods to wield unimaginable power.
Reject
Reject is the yin to .select’s yang:
1
2
cars.reject{|car| valid_colors.include?(car[:color]) }.map{|car| car[:type]}
# => ["mustang"]
Instead of selecting for the array items we want, we will reject everything that does not make our function return true. Remember that the function inside our reject is what determines if the array item will be returned or not — if it’s true, the item is returned, otherwise not.
Reduce
Reduce has a more complex structure than our other array methods, but it’s generally used for pretty simple things in Ruby — mostly math stuff. We’ll take an array, then run a function on every item in that array. This time, we care about what is being returned from the other array items. Typically we are adding up a bunch of numbers:
1
2
3
array = [1, 2, 3]
array.reduce{|sum, x| sum + x}
# => 6
Note that we can work with strings in the same way:
1
2
3
array = ['amber', 'scott', 'erica']
array.reduce{|sum, name| sum + name}
# => "amberscotterica"
This might be helpful if we are looking at a bunch of work records. If we need to add up total hours worked, or if we want to
find out the sum of all donations last month. One final note about .reduce.
is if you’re working with anything other than
regular old numbers (or strings), you’ll need to include a starting value as an argument:
1
2
3
4
5
array = [{weekday: 'Monday', pay: 123}, {weekday: 'Tuedsay', pay: 244}]
array.reduce(0) {|sum, day| sum + day[:pay]}
# => 367
array.reduce(100) {|sum, day| sum + day[:pay]}
# => 467
Join
Talking about .join
as a bonus because it’s so dang useful. Let’s use our array cars
again:
1
2
cars.map{|car| car[:type]}.join(', ')
# => "porsche, mustang, prius"
.join
is a lot like .reduce
except it’s got a super-clean syntax. It takes one argument: a string that will be inserted
between all array elements. .join
creates one long string out of whatever you give it, even if your array is a bunch of non-string
stuff:
1
2
3
4
cars.join(', ')
# => "{:type=>\"porsche\", :color=>\"red\"}, {:type=>\"mustang\", :color=>\"orange\"}, {:type=>\"prius\", :color=>\"blue\"}"
events.join(', ')
# => "#<Event:0x007f9beef84a08>, #<Event:0x007f9bf0165e70>, #<Event:0x007f9beb5b9170>"
Slice
slice
is a method that operates on arrays, strings, and (since Ruby 2.5.0) hashes. We’ll just focus on arrays for now, since
the logic is basically the same regardless, but keep in mind that you can call slice
on strings and hashes as well. slice
allows you to cut into an array and select specific elements.
Let’s say you have an array of fruits:
fruit = [“apple”, “banana”, “orange”, “grapefruit”, “tomato”]
You can call slice
on the array, pass it a specific index, and it will return the object at that index. Calling fruit.slice
with an argument of (2) will return the string object “orange” because “orange” is at index 2 of the fruit array (note that array
elements are indexed starting at 0, so “apple” is at index 0, “banana” is at index 1, etc). Let’s drop into IRB and check it out:
1
2
3
4
fruit = ["apple", "banana", "orange", "grapefruit", "tomato"]
# => ["apple", "banana", "orange", "grapefruit", "tomato"]
fruit.slice(2)
# => "orange"
If you want the banana, the orange, and the grapefruit, you can call slice
with a starting index and a length of elements
and it will return those objects in an array.
1
2
3
4
fruit = ["apple", "banana", "orange", "grapefruit", "tomato"]
# => ["apple", "banana", "orange", "grapefruit", "tomato"]
fruit.slice(1,3)
# => ["banana", "orange", "grapefruit"]
This selects all elements starting with index 1 and ending with index 3.
Similarly, we can use a range
1
2
fruit.slice(1..3)
# => ["banana", "orange", "grapefruit"]
Cycle
cycle
is a Array class method which returns the array by calling the given block each time for every element in the array ‘n’
no. of times and if ‘nil’ is given, then it will call it for forever.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# declaring array
a = [18, 22, 33, 5, 6]
# declaring array
b = [1, 4, 1, 1, 88, 9]
# declaring array
c = [18, 22, nil, nil, 50, 6]
# cycling the array elements
puts "cycle : #{a.cycle(3){ |x| puts x*x }}\n\n"
# cycling the array elements
puts "cycle : #{b.cycle(2){|x| puts x}}\n\n"