Erik Trautman logo crest shield seal

Erik Trautman

“Everything you can imagine is real.”
-- Pablo Picasso

Ruby Explained: Arrays

This post will get into Arrays, which is where you really start seeing some of Ruby’s cool programmer-friendly features

Arrays are almost as ubiquitous as strings. You’ll be working with them all the time to help store data, everything from the names of all your users to coordinates on a game board. An array is an all-purpose bucket into which you can put pretty much anything.

Here, you’ll learn the basics of creating arrays, how to manipulate them in a dozen different ways, and some best practices for working with arrays. Note that we’ll be learning even more about how to dig around inside of arrays in a future lesson, so if you’re excitedly waiting to better understand #each, #map and others like them, we’re almost there! If not… you will be.

There are tons of methods designed to help you poke around in or otherwise manipulate arrays and you’ll be seeing plenty of them here. Don’t worry if it starts to feel like you’re having to remember too much stuff. It can be helpful to make a cheat sheet for yourself to help remember but you’ll end up using the same dozen or so methods again and again so you’ll get them hammered in there pretty well. As with many things in Ruby, if you forget the method name but think it should exist, just make a guess and try it and you’ll probably be right.

Arrays begin life as empty containers waiting to be filled with objects or data. As items are added, they stay in whatever spot you put them, which is good because then you know exactly where to find them later. You can put anything in an array! Numbers, strings, objects, symbols, haikus…

Creating an Array can happen in many different ways. You can either create it empty, specify how many spaces it should have (still empty), or even fill it with default values:

> a = Array.new     # 1
=> []               # see, it's empty
> b = []
=> []               # still empty
> c = Array[]       
=> []
> d = %w{ I am not a crook }  # converts the string (no quotes) to an array
=> [ "I", "am", "not", "a", "crook" ]
> empty_a = Array.new(5)
=> [nil, nil, nil, nil, nil]
> full_a = Array.new(3, "hi")
=> ["hi", "hi", "hi"]

And remember, you can store pretty much anything in there, even other arrays:

> full_b = [1, 4, 8, "hello", a]
=> [1, 4, 8, "hello", []]  # Cool, arrays inside arrays!

but don’t do that! It’s best to keep only ONE type of thing in your arrays or you’ll have many headaches down the road because you’ll almost always assume that there’s only one type of thing in there. Forget that you ever learned that arrays can hold different values.

Accessing Items is super easy, just start from 0 like you did with strings. Just like with strings, you can start from the end of the array using negative numbers from -1 and you can even grab ranges of values at a time:

> arr = [1, 3, 5, 7, 2]   # favorite way to declare an array
=> [1, 3 ,5 ,7 ,2]
> arr[0]
=> 1
> arr[-1]
=> 2
> arr[1..3]
=> [3, 5, 7]          # this returned an array!
> arr.slice(1..3)
=> [3, 5, 7]          # same as using [1..3]
> arr[1..200000]      
=> [3, 5, 7, 2]       # no error... silently cuts off at the end

Modifying Items is as simple as accessing them is… just set them equal to a value:

> arr[0] = 42
=> 42
> arr
=> [42, 3, 5, 2]      # changed it!
> arr[0..2] = 99
=> 99
> arr
=> [99, 2]            # wiped out several values, oops...

Adding Arrays is also done similarly to strings, by just mashing one onto the end of the other:

> first = [1,2,300]
=> [1,2,300]
> second = [7,8,9]
=> [7,8,9]
> combined = first + second
=> [1,2,300,7,8,9]      # this is a NEW array

Subtracting Arrays is a bit different… think of the minus sign as saying "take away any and all values that are duplicated in the right array from the left array". The only values remaining will be those from the left that were not included in the right side at all:

> [1,2,3] - [2,3,4]
=> [1]                # the 4 did nothing
> [2,2,2,2,2,3,4] - [2, 5, 7]
=> [3,4]              # it killed ALL the 2's

You’ll find yourself adding arrays a lot more frequently than subtracting them but it’s good to know both.

If you want to find values in Both arrays, check their union using the ampersand &:

> [1,2,3]&[2,4,5]
=> [2]

What if you only want to add or subtract one single value? That’s a very common operation with arrays, and Ruby has provided four handy methods that let you either pluck away or add onto the front or back of the array. First, the more common is to add or remove stuff from the END of the array, using #push or #pop:

> my_arr = [1,2,3]
=> [1,2,3]
> my_arr.push(747)
=> [1, 2, 3, 747]
> my_arr
=> [1, 2, 3, 747]     # warning: we modified my_arr!!!
> my_arr.pop
=> 747
> my_arr.pop
=> 3
> my_arr
=> [1, 2]          # warning: pop also modified my_arr

What if you want to take the item off the FRONT of the array? This is less common. For that, use the similar #shift and #unshift methods:

> my_arr = [1,2,3]
=> [1,2,3]
> my_arr.shift
=> 1
> my_arr
=> [2,3]          # warning: shift also modified my_arr!
> my_arr.unshift(999)
=> [999, 2, 3]
> my_arr
=> [999, 2, 3]    # warning: unshift... yep, modified my_arr.

So the #push/#pop/#shift/#unshift methods should take you wherever you realistically need to go. Although there’s another handy method you should be aware of, the Shovel Operator, aka <<. This method is almost identical to push, since it just jams whatever’s to its right into the array:

> my_arr = [1,2,3]
=> [1,2,3]
> my_arr << 3
=> [1, 2, 3, 3]
> my_arr << [4,5]
=> [1, 2, 3, 4, [4, 5]]   # Array within array alert!

For now, just think of it as the cool way of pushing onto an array. But note that << is often overridden (like in Rails), and so it pays to be mindful of exactly what flavor of pushing you’re doing.

Deleting Items from an array should be done carefully because, if you’re deleting items inside a loop or something like that, it will change the index of the other items and you’ll need to anticipate this or live to regret it. Delete an item at a specific index using #delete_at, which is sort of like poping but from anywhere you want:

> my_arr = [1,2,3]
=> [1, 2, 3]
> my_arr.delete_at(1)
=> 2
> my_arr
=> [1,3]

If you want to clear out the whole array, you can use #clear or, more easily, just set it equal to []:

> my_arr = [1,3,5]
=> [1,3,5]
> my_arr.clear
=> []
> my_arr = [1,3,5]
=> [1,3,5]
> my_arr = []           # better
=> []                   

See if an array includes an item AT ALL by using #include?, which, as you should see from the ? at the end, returns true or false:

> my_arr.include?(3)
=> true
> my_arr.include?(132)
=> false

To find WHERE a specific item lives in the array, use #index but note that it only returns the FIRST instance of this (and then gives up. Lazy method.):

> my_arr.index(3)
=> 2
> [1,2,3,4,5,6,7,3,3,3,3,3].index(3)
=> 2                  # Just the index of the FIRST match
> my_arr.index(132)
=> nil                # Not an error, just nil

A few useful and commonly used methods:

  • #max to find the biggest value of an array
  • #min to find the smallest value of an array
  • #uniq to remove all duplicates from your array
  • #size to find out how big the array is
  • #shuffle will mess up your whole array by putting it in random order
  • #sort will clean it up again for you by putting your array in order. Though #sort is pretty self-explanatory in the simple case, it can actually take parameters to let you decide if you want to sort things using a different (or reverse) methodology.
  • #sample picks out a totally random value from the array… good for gambling games!
  • #first gives you the first item (but doesn’t remove it, so it’s same as [0]) but can be more descriptive of your code’s intent.
  • #last is same as [-1]

Do as I say and not as I do: name your arrays with the plural form (because it has a bunch of things in it, like colorful_bugs instead of colorful_bug) and be descriptive. No one likes to try and figure out what array1 or a contains… stick with colorful_bugs I just kept them short here because they’re tiny examples. Someone should rename them all.

Strings are a lot like arrays… so much so that we can even Convert an Array into a String! Just use #join and tell it what, if anything, you want in between each element (the "separator"):

> ["he", "llo"].join
=> "hello"
> colorful_bugs = ["caterpillar", "butterfly", "ladybug"]
=> ["caterpillar", "butterfly", "ladybug"]
> "I found a #{colorful_bugs.join(' and a ')} in the yard!"
=> "I found a caterpillar and a butterfly and a ladybug in the yard!" 

Want to know a cool way to make an array? Create it from a Range and just Convert it to an Array:

> my_awesome_array = (1..6).to_a
=> [1,2,3,4,5,6]

Advanced stuff (you don’t need to know this right now):

Remember how we could create a new array and fill it up with stuff using Array.new(5, "thing")? Array.new also takes an optional argument that is a block and it will run that block every time it needs to populate a new element. Woah! We got a bit ahead of ourselves, but it’s a cool feature to have floating in the back of your head.

> Array.new(5){|item_index| item_index ** 2}
=> [0, 1, 4, 9, 16]    # It squared each index to populate the array!

*The “Ruby Explained” posts are designed to be a sort of “In-Plain-English” version of key Ruby concepts which are usually covered in other introductory texts but rarely for free and often incompletely. When I’m learning a new thing, I usually want someone to explain it to me like I’m a five year old because that’s the best way to make sure nothing gets missed. This is my attempt to pass that same sentiment on to you. Let me know if there’s anything I can improve.*

If you’re just getting interested in this stuff, check out The Odin Project for a free curriculum to learn web development.