These free mini-courses will give you a strong foundation in web development. Track your progress and access advanced courses on HTML/CSS, Ruby and JavaScript for free inside our student portal.
Scroll down...
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.
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 (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 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 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 intersection using the ampersand &
:
> [1,2,3]&[2,4,5]
=> [2]
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
<<
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.
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 push
ing you're doing.
In general, you'll use <<
instead of push
because it's cooler.
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 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 pop
ing 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
#=> []
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
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...
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 arraymin
to find the smallest value of an arrayuniq
to remove all duplicates from your arraysize
to find out how big the array isshuffle
will mess up your whole array by putting it in random ordersort
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.
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
.
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!"
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!