Learn How to Use Ruby Arrays in Less Than 10 Minutes

Jul 3, 2018 - 9 min read
Learn How to Use Ruby Arrays in Less Than 10 Minutes
Share

Arrays are probably the most popular data structure you’re going to use in Ruby. They allow you to group data as a list of elements.

The elements in the array don’t have to be of the same type. They can be anything. Even other arrays if you want.

How to create an array

Here’s an example of a Ruby array.

[1, 2, "a", :b, [3, 4]]

You can also create one using the new method. It can take an initial size, and the default object. If you don’t specify any arguments, it creates an empty array.

Array.new(2, :a) # => [:a, :a]
Array.new # => []

The second argument to Array.new (i.e. the object) is used to populate the array with references to that object.

my_array = Array.new(2, :a)
my_array.first.object_id # => 751068
my_array.last.object_id # => 751068

If you want to initialize the array with separate objects, you need to pass it a block.

Array.new(3) { |e| e + 4 } # => [4, 5, 6]

The fourth, and last method of creating an array is by using the Kernel.Array() method, or simply Array(), and passing it an object. The Array() method will then try to call to_ary, and then to_a on the object.

Array(1) # => [1]
Array("a") # => ["a"]
Array({"a": 1, "b": 2}) # => [[:a, 1], [:b, 2]]

How to access array elements

Element indexing starts at 0. So if you want to access the first element in the array you could do so by using one of the following methods [], slice, and at. I’ll show an example in just a second.

You could also use a negative index to access the elements from the end of the array. So you could use -1 to access the last element, and -2 to access the second to last element, and so on.

Ruby also provides some syntactic sugar for accessing the first (index 0) and last (index -1) elements. Namely the first and last methods.

Here’s how you would use them.

my_array = ["z", 2, "a", :b, [3, 4]]

my_array[0] # => "z"
my_array.first # => "z"
my_array.at(1) # => 2
my_array.slice(2) # => "a"
my_array[-1] # => [3, 4]
my_array.last # => [3, 4]

There’s another way you can access the elements. And that is through the fetch method. The nice thing about fetch, is you can tell it how to fail.

If you access an element that doesn’t exist with any of the methods presented above (except fetch), you get nil back. But with fetch, you can either get an error, or you can tell it what to return if it fails.

my_array = [:a, :b]

my_array.fetch(1) # => :b
my_array.fetch(4)
# => IndexError: index 4 outside of array bounds: -2...2
my_array.fetch(4, "Don't go beyond #{my_array.size-1}")
# => "Don't go beyond 1"

How to retrieve multiple elements

What you’ve seen by now is how to get back just one element. But what if you want to get multiple elements back.

You can either specify a start and end index, or a range.

my_array = [1, 2, 3, 4]
my_array[2, 2] # => [3, 4]
my_array[0..1] # => [1, 2]
my_array[2..-1] # => [3, 4]

Note how you can use -1 in a range for the last element. How cool is that.

But wait, there’s more 🙂

The take method is used to get the first n number of elements. So if you want to get the first 3 elements, you can use take(3).

[1, 2, 3, 4, 5].take(3) # => [1, 2, 3]

Likewise, you can get the rest of the list by dropping the first n elements with the drop method.

[1, 2, 3, 4, 5].drop(2) # => [3, 4, 5]

Check if a value exists

There are a few ways you might want to check for values.

The first and most obvious is to see if an element exists at a particular index. And you already know how to that.

[1, 2, 3].slice(0) # => 1
[1, 2, 3].slice(5) # => nil

But what if you want to know if a particular value exists in the array. Say for example you want to know if the array contains the number 4.

[1, 2, 3].any? { |n| n == 4 } # => false

Another method that can check if the array contains a particular value is include?.

[1, 2, 3].include?(4) # => false

But what about if you want the value back. For example, you might want to know what element of the array starts with the letter “d”.

["abc", "bcd", "dfg", "ghk"].find { |e| e =~ /^d.*/ } # => "dfg"

And if instead of returning the actual element, you want it’s index, you can use find_index.

["abc", "bcd", "dfg", "ghk"].find_index { |e| e =~ /^d.*/ } # => 2

How to sum array of numbers

There are two ways you can achieve the same thing. You should pick the method you prefer depending on your context. The simplest way is with sum.

[1, 2, 3].sum # => 6

Note that sum adds elements together. So if you call it on an array of strings, it will still work.

["a", "b", "c"].sum("") # => "abc"

You can also use inject or reduce. They combine the elements of the array by applying the binary operation you provide.

[1, 2, 3].inject(:+) # => 6
[1, 2, 3].reduce {|sum, n| sum + n } # => 6

If you use the block version, you get an accumulator (e.g. sum) as the first argument to the block.

reduce and inject are aliases, so you can use whichever you like.

How to pick randomly from an array

You can use the sample method to pick one or more random elements from the array. By passing it an argument, you can specify the number of random elements you want.

[1, 2, 3, 4, 5, 6].sample # => 4
[1, 2, 3, 4, 5, 6].sample(3) # => [2, 1, 4]

Removing duplicate elements

Back when I didn’t know better, I used to do this with a hash. Here’s how I did it (just for fun).

[1,1,2,2,3].inject({}) { |h, n| h[n] = nil; h }.keys
# => [1, 2, 3]

But there is no point in going through all that trouble. Ruby provides a uniq method that returns a new array without any duplicates.

[1,1,2,2,3].uniq # => [1, 2, 3]

There is also a destructive uniq! method that will change the object in place (i.e. it won’t create a new array).

Sorting an array

Ruby provides a sort method that will default to sorting your array by using the <=> operator.

[3, 2, 4].sort # => [2, 3, 4]

Sorting in descending order

You can pass a block to sort telling it how to do the comparison. So to sort in descending order, all you need to do is to switch the placement of the operands (i.e. b <=> a, instead of a <=> b).

[3, 2, 4].sort { |a, b| b <=> a } # => [4, 3, 2]

Or, you could do the regular sort and then reverse the array.

[3, 2, 4].sort.reverse

But there’s another way. You can use the sort_by method, which is more efficient (because it uses the Schwartzian algorithm) when you’re dealing with more complex data structures like objects or collections.

names = [{name: "John"}, {name: "Jane"}, {name: "Bill"}]
names.sort_by { |h| h[:name] }
# => [{:name=>"Bill"}, {:name=>"Jane"}, {:name=>"John"}]

How to remove blank elements from an array

Imagine you got an array from the database and you want to clean it up and remove the empty elements. Well, you’re in luck because that’s easy to do in Ruby.

["a", "", "b", " "].reject(&:empty?) # => ["a", "b", " "]

But that’s probably not 100% what you want. That empty string there is something you would prefer to take out.

["a", "", "b", " "].reject { |e| e.strip.empty? } # => ["a", "b"]

That’s better.

How do you add arrays together

Let’s say you want to add two arrays together. That’s simple.

[1, 2] + [2, 3, 4] # => [1, 2, 2, 3, 4]

But I’m betting that’s not exactly what you wanted. The number 2 is duplicated now. So to remove duplicates you can use what’s called a set union.

[1, 2] | [2, 3, 4] # => [1, 2, 3, 4]

Both of those methods will create a new array. But if you want to change an existing array, you need to look at concatenation.

my_array = [1, 2]
my_array += [2, 3, 4] # => [1, 2, 2, 3, 4]

How to combine array into strings

If you want to learn more about strings, you can check out the article on working with strings in Ruby. But if the only thing you need is to convert an array into a string, here’s how to do it.

["a", "b", "c"].join # => "abc"
["a", "b", "c"].join(" ") # => "a b c"

And to convert a string into an array, you can use chars (not exactly sure why to_a is not available).

"abc".chars # => ["a", "b", "c"]

What is the right way to iterate through an array in Ruby?

There are a few ways actually and you need to choose the one you prefer based on the result you’re looking for.

  • each: for when don’t often care about the indexes
  • each_ with _index: you get the value and the index
  • each_index: just the indexes
  • map: useful when you want to transform one array into another
  • select: when you want to choose a subset
  • inject: for generating sums or products, or collecting a single result

You can learn more about how to use each and each_with_index.

Ruby has different ways of achieving the same goal and each has its pros and cons given a particular context. So read the docs, and chose the best one for the job.

12 Project Ideas
Cezar Halmagean
Software development consultant with over a decade of experience in helping growing companies scale large Ruby on Rails applications. Has written about the process of building Ruby on Rails applications in RubyWeekly, SemaphoreCI, and Foundr.