Arrays

All the useful ways Ruby lets you play with this ubiquitous data structure.

Scroll down...

Content

Resources

Comments

In this lesson we'll cover Arrays.

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. It'll be a chance to dive into some of Ruby's cool programmer-friendly features.

There are all kinds of Ruby 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 but in the real world you'll end up using the same dozen or so methods again and again. 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.

Note that we'll be learning even more about how to dig around inside of arrays in the lesson on Iteration. If you're excitedly waiting to better understand each, map and others like them, we're almost there! If not... you will be.

What is an Array?

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

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

> a = Array.new  
#=> []
> b = []
#=> []
> c = Array[]       
#=> []
> d = %w{ I am not a crook }  # Array from String
#=> [ "I", "am", "not", "a", "crook" ]
> empty_e = Array.new(5)
#=> [nil, nil, nil, nil, nil]
> full_f = Array.new(3, "hi")
#=> ["hi", "hi", "hi"]
> range_g = (1..6).to_a
#=> [1,2,3,4,5,6]

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! With great power comes great responsibility. 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 you ever learned that arrays can hold different values.

How should you create it? Just use the easiest possible syntax... either create it empty with the literal brackets or fill it immediately:

> my_empty_arr = []               # Good!
#=> []
> my_full_arr = [1,5,25,2]  # Good!
#=> [1,5,25,2]

Reading Items

Reading 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]   # best creation method
#=> [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

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

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

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.

Intersection of Two Arrays

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

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

Adding To or Removing From the Back

Adding or removing items from the end of an array are very common operations and Ruby has provided some handy methods that let you do so. Note that it matters whether you want to add or remove the item from the front of the array or the back.

It's much more common to add to or remove from the END of the array using push or pop.

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

As you saw above, popping from an array is a destructive operation! If you just want to READ the last value, use the simpler methods:

> nums = [1,2,3]
#=> [1,2,3]
> nums.last
#=> 2
> nums[-1]
#=> 2

The Shovel Operator <<

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 is to its right into the array:

> nums = [1,2,3]
=> [1,2,3]
> nums << 3             # Shovel it in there!
=> [1, 2, 3, 3]
> nums << [4,5]         # Hmm, let's try this... 
=> [1, 2, 3, 4, [4, 5]]   # Array within array alert!

For now, just think of << as the cool way of pushing onto an array.

The Shovel Operator is Also Destructive

Just like push, the shovel operator is a destructive operation, meaning that it modifies the original array instead of making a copy of it first:

> nums = [1,2,3]
#=> [1, 2, 3] 
> nums.object_id
#=> 70276155431520 
> nums << 4
#=> [1, 2, 3, 4] 
> nums.object_id
#=> 70276155431520 
> nums += [5]        # Creates a new array in memory!
#=> [1, 2, 3, 4, 5] 
> nums.object_id
#=> 70276155396000 

We'll explore this phenomenon a bit more in a later lesson. You don't need to change your behavior, just keep it in the back of your mind.

It's also worth remembering that, just like + or - or ==, << is just a method. It can be overridden (for instance in Rails), and so it pays to be mindful of exactly what flavor of pushing you're doing.

In general, you'll use << instead of push because it's cooler.

Adding To or Removing From the Front

What if you want to take the item off the FRONT of the array? This is less common. In some ways, you can think of arrays like stacks of vinyl records... it's more difficult to remove the bottom record from the pile so you typically remove the topmost one first.

To remove the first (or "bottom") item, use shift and unshift, which behave exactly like push and pop, just on the other end:

> nums = [1,2,3]
#=> [1,2,3]
> nums.shift
#=> 1
> nums
#=> [2,3]          # warning: nums is modified!
> nums.unshift(999)
#=> [999, 2, 3]
> nums
#=> [999, 2, 3]    # warning: nums is modified!

Deleting Items

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.

To delete an item at a specific index, use delete_at. It's sort of like poping but from anywhere you want:

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

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

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

Checking if an Item is Included

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:

> nums = [1,3,5]
#=> [1,3,5]
> nums.include?(3)
#=> true
> nums.include?(132)
#=> false

Searching For An Item

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

> nums.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
> nums.index(132)
#=> nil      # Not an error, just nil...

Simple and Useful Methods

You'll use these methods quite frequently. Luckily they're very common sense and so shouldn't be hard to remember.

  • 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]
> nums = [1,3,5,7,6,4,2,4,4,4,5]
#=> [1,3,5,7,6,4,2,4,4,4,5]
> nums.max
#=> 7
> nums.min
#=> 1
> nums.uniq
#=> [1,3,5,7,6,4,2]
> nums.size
#=> 11
> nums.shuffle
#=> [7, 5, 2, 4, 4, 3, 1, 6, 4, 4, 5]
> nums.sort
#=> [1, 2, 3, 4, 4, 4, 4, 5, 5, 6, 7]
> nums.sort{|first,second| second <=> first } # Just for fun
#=> [7, 6, 5, 5, 4, 4, 4, 4, 3, 2, 1] 
> nums.reverse
#=> [5, 4, 4, 4, 2, 4, 6, 7, 5, 3, 1]
> nums.sort.reverse
#=> [7, 6, 5, 5, 4, 4, 4, 4, 3, 2, 1]
> nums.sample
#=> 4
> nums.first
#=> 1
> nums.last
#=> 5

Note that many of these items (like sort and reverse) also have a bang ! version which modifies the original array. Use sparingly.

Naming Arrays

Name your arrays with the plural form because it's a container with many things in it, like colorful_bugs instead of colorful_bug. No one likes to try and figure out what array1 or a contains... stick with colorful_bugs.

Stringifying an Array

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!" 

Advanced Trickery: Smart Array Population

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!

You probably won't pull this out at many cocktail parties but it's food for thought. Lots of Ruby methods actually take blocks to extend their functionality like this.

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


Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

Sorry, comments aren't active just yet!