An array is a list of items in order (like vitamins, minerals, and chocolates).
Those keeping score at home might be interested to know that the Rails website framework makes 771 calls to Array.each, 558 calls to Array.map, and 1,521 calls to Array.empty?, not to mention the 2,103 times it accesses a single element inside an array.
But these are just numbers. How do you use arrays in Ruby?
Creating Arrays
Most of the time, you will be working with arrays of database objects. Other times, you will need to make a simple array of your own.
If you have used Perl, PHP, Python, or other scripting languages, the syntax of Ruby might seem at least remotely familiar to you. If you have installed Ruby on your computer, you can also use the “irb” command-line program, which runs lines of code interactively.
You can make an array by using square brackets like this:
# Make a list of clues for a game of charades
clues = ['vitamins', 'minerals', 'chocolates']
# Create the same array by splitting on whitespace
clues = %w(vitamins minerals chocolates)
# Create the same array in steps
clues = Array.new
clues << 'vitamins'
clues << 'minerals'
clues << 'chocolates'
You can retrieve values from an array by number, starting at zero. In these examples I’ll use =>
to show the result of each line.
a_clue = clues[0]
=> 'vitamins'
another_clue = clues[1]
=> 'minerals'
Ruby also has some shortcuts for commonly accessed attributes of an array:
a_clue = clues.first
=> 'vitamins'
another_clue = clues.last
=> 'chocolates'
clues.length
=> 3
Ok … enough of the basics. What can you do with an array?
each and each_with_index
Many languages force you to handle arrays with arcane sequences of semicolons, parentheses, and placeholders. Not Ruby!
my_vitamins = ['b-12', 'c', 'riboflavin']
my_vitamins.each do |vitamin|
puts "#{vitamin} is tasty!"
end
=> b-12 is tasty!
=> c is tasty!
=> riboflavin is tasty!
As you might guess, this prints a message for each of the vitamins. Here are a few things to remember:
- The variable “vitamin” inside the “goalposts” refers to each item in the array as it goes through the loop. You can give this any name you want, but make it memorable.
- The “do” and “end” identify a block of code that will be executed for each item. Blocks are used extensively in Ruby.
- The “#{vitamin}” inside the double-quotes is how you insert a variable inside a string. There are other ways to do it, but “#{}” is the simplest.
What if we wanted to print the name of the vitamin and also do something with its place in the array?
my_vitamins.each_with_index do |vitamin, index|
puts "#{index} cheers for #{vitamin}!"
end
=> 0 cheers for b-12!
=> 1 cheers for c!
=> 2 cheers for riboflavin!
Again, the names “vitamin” and “index” inside the vertical bars are names that you choose. However, the “each_with_index” method calls the block with two values, so you need to provide two variable names. Several useful references can be found at Ruby-Doc.org.
If this were production code, we might write an additional method to apply proper English grammar based on the number of cheers (Rails has such a method that we could use). We might also want to use a more authoritative source for determining how many cheers each vitamin should get!
map
Ruby’s ability to work with arrays doesn’t stop there. Array.map calls a block for each item and returns a new array based on the result of the block.
nouns = ['truffle', 'kiss', 'rabbit']
array_of_chocolates = nouns.map do |noun|
"chocolate #{noun}"
end
# array_of_chocolates now has these values
=> ['chocolate truffle', 'chocolate kiss', 'chocolate rabbit']
You might notice that we don’t have to use the “return” keyword … Ruby automatically returns the last value in a block. In this case, the return value from each iteration of the block is the new “chocolate something” string. However, we could have returned a number or any other kind of object.
On my blog I’m writing a Rails plugin to take data from my Mint stats installation and show a list of popular pages. I use “map” to put the numbers together.
# Get an array of rows from a database query
stats = Mint.popular_pages
# Assemble a summary for each
stats_report = stats.map do |stat|
"#{stat.resource_title} was visited #{stat.visit_count} times"
end
# stats_report is now an array with these values:
=> ['Home was visited 1200 times', 'Search was visited 900 times', ...]
The resource_title
and visit_count
methods are actually fields in the database. ActiveRecord looks at the database schema and creates these methods automatically. I use these to build a string for each row which is returned as a new array.
inject
The inject method is one of my favorites even though it is a bit complicated (Ruby borrowed it from the Smalltalk language). Let’s look at an example and then I’ll explain how it works:
# Get an array of rows from a database query
stats = Mint.popular_pages
# Add statistics to get total number of visits
total_visits = stats.inject(0) do |sum, stat|
sum = stat.visit_count
end
=> 8800
Several things are happening here:
- We start with an array of database row objects, as seen earlier.
- Inject takes an argument, which in this case is a number. This is the starting value that will be manipulated during each iteration of the block and returned at the end. We could have started with a blank array (“[]”), a blank hash (“{}”), or even an existing object.
- The arguments to the block are the variable we just initialized, and a single element from the array.
Still confused? That’s ok … let’s step through it.
To begin with, “sum” will be zero because we sent “0” as the initializing value to inject
.
“stat” will be the first item from the “stats” array. We add that page’s number of visits to the current “sum”.
That cycle repeats for all the elements in the array, gradually increasing the overall “sum”.
At the end, the final value of “sum” is returned (8,800 in this case).
So that’s a lot of functionality in a very small amount of code. In fact, you could fit it all on a single line with the optional curly-bracket syntax:
total_visits = stats.inject(0) {|sum, stat| sum = stat.visit_count}
Operators – and
Arrays are capable of much more, but here is a final feature: addition and subtraction. Adding arrays simply combines them, even if there are duplicates. Array subtraction is smart enough to take out the duplicates.
a = [1, 2, 3]
b = [1, 4, 5]
a - b
=> [2, 3]
a b
=> [1, 2, 3, 1, 4, 5]
(a b) - a
=> [4, 5]
Many of these methods are available to any object that has its own “each” method! See Ruby-Doc or the older online version of Programming Ruby for the details.
And tune in next time for the basics of Ruby hashes!
Where is the actual addition happening in “total_visits = stats.inject(0) {|sum, stat| sum = stat.visit_count}” (or in the longer version above?)
I want print all elements in a multi dimensional array.
Is there any way to do it?
Thanks for the run down, I am learning ruby and this will be very useful.
I like your blog and such a useful information sharing. I appreciate your blog and thanks for that.
– Magento Templates
The car’s suspension system includes leaf springs, a stabilizer bar, a suspension arm, a shock absorber and/or MacPherson struts.
it is really nice and useful
An exhausting line-by-line example of inject: http://blog.jayfields.com/2008/03/ruby-inject.html
Thanks for posting this however it looks like you have an error in the inject example. I tried your example and kept getting the sum equal to the visit_count of the last item in the array. Instead of:
total_visits = stats.inject(0) {|sum, stat| sum = stat.visit_count}
it should be (notice the “+” in the block):
total_visits = stats.inject(0) {|sum, stat| sum + stat.visit_count}
http://ruby-doc.org/core/classes/Enumerable.html#M003129 has the documentation for this.
Again, thanks for posting…you got me headed in the right direction.
Am a newbie with Ruby. I’ve been through so many references and yours is helping me learn array in simplest terms. Thank you so much.
How do I use the form of –
my_vitamins.each do |vitamin|
puts “#{vitamin} is tasty!”
end
to print just the first x elements from the array ?
(i.e instead of “each”, I only want the first x elements)
Thanks.
Jason
Something like that ?
y = 3
y.times do |x|
puts “#{my_vitamins[x]} is tasty!”
end
With a little modification you can narrow down the full array before iterating through it.
my_vitamins[1..x].each do |vitamin|
puts “#{vitamin} is tasty!”
end
the previous two examples will start with the second element in the array.
if you want to print the first three elements of the array use this:
my_vitamins[0..2].each do |vitamin|
puts “#{vitamin} is tasty!”
end
Thank you for posting nice article on Ruby Array it's very use full for me.
I've got a great start now.. 🙂 Ruby used to scare me. lol
I've got a great start now.. 🙂 Ruby used to scare me. lol
Very well done – you've taken something very complex and broken it down so well I think I finally get it! Thanks 🙂
Pharmacy providing brand name and generic prescriptions drugs with worldwide delivery.
Very well done – you've taken something very complex and broken it down so well I think I finally get it! Thanks 🙂Pharmacy providing brand name and generic prescriptions drugs with worldwide delivery.