Array of Sunshine and Hash on the Range - Ruby Wizardry: An Introduction to Programming for Kids (2014)

Ruby Wizardry: An Introduction to Programming for Kids (2014)

Chapter 5. Array of Sunshine and Hash on the Range

Big Hank’s Hashery

“Morning, Your Majesty!” boomed a voice from deep inside the Hashery.

“Good morning, Big Hank!” said the King.

“Big Hank?” asked Ruben. “Who’s Big Hank?”

An enormous man with a bald head and a curly black mustache emerged from the back of the restaurant. “I am!” he said.

image with no caption

The King shook Hank’s hand vigorously. “Great to see you, Hank! We had a doodle of a time getting here—the Loop was acting up—but I can’t wait to sit down to a fine meal of your best hash.”

Big Hank frowned, and his mustache drooped noticeably. “The Loop’s gone loopy?” he asked. “I wish I could say I were surprised. Things have been going a little haywire here, too.”

The King gasped. “You don’t mean—”

Big Hank nodded. “Our range is on the fritz,” he said. “Until we fix it, I won’t be able to cook up anything: no eggs, no breakfast gravy, and certainly none of my famous hash.”

The King slumped onto one of the many long oak benches filling the Hashery. “No hash! What could be worse?”

“This hash must be really good,” Scarlet said.

“It’s the best!” shouted the King, who was on the verge of tears. “But without a working range, there won’t be any. And we’ve come all this way!”

“Now, hang on,” said Big Hank. “This isn’t the first time Squeaky Jim and I have gotten into a pickle here at the Hashery, and it won’t be the last. We’ll get this figured out.”

“Who’s Squeaky Jim?” asked Ruben.

“He’s my fry cook,” Hank said. “He’s not what you’d call a whiz with the kitchen technology—it all runs on Ruby—but he’s a heck of a cook. Makes a great omelette, and he’s almost mastered my hash recipe.”

“Ruby!” Ruben and Scarlet shouted together.

Big Hank raised a heavy black eyebrow. “Do you kids know anything about Ruby?” he asked. “That would be a huge help.”

“Absolutely!” said Scarlet. “Show us the kitchen, and we’ll take it from here.”

“Hooray!” said the King. “These kids are as smart as a whip, Hank,” he added. “They’ll have your kitchen up and running in no time.”

Big Hank nodded. “Sounds good! I’ve actually got Squeaky Jim using the old griddle in the back, but I think he could use some Ruby help. I’ll take another crack at the range, but if you kids and Jim get the orders flowing before I get it fixed, give me a shout and we’ll put our heads together.”

“Sure thing,” Scarlet said. “Lead the way!”

Big Hank motioned for them to follow him and lumbered through row after row of wooden benches toward the back of the Hashery. He stopped at a red metal door with a small window toward the top, tapped on it twice, and shouldered it open. “Jim!” he called. “The King and his friends are here!”

They heard a brief scuffling sound in the corner of the kitchen, followed by the crashing of a dozen or so pots and pans.

“It’s okay, it’s okay—I’ve got it!” Squeaky Jim called, his voice cracking twice. He stumbled out from behind a large pile of potato sacks, with a saucepan in each hand and one perched crookedly on his head.

“I know why they call him Squeaky Jim,” Ruben whispered to Scarlet.

“Easy, Jim,” Big Hank said, taking the pans from Jim. “The King and his friends know a thing or two about Ruby, so I’m asking them to help you out while I tinker with the main range.”

Squeaky Jim hastily bowed to the King. “Your Majesty,” he said.

“Squeaky Jim,” said the King.

image with no caption

Big Hank gestured to the corner of the kitchen Squeaky Jim had just come from. “You’ll find the Computing Contraption over there,” he said. “I’ll be at the range, on the other side of the kitchen.” He turned and hefted a sack of potatoes under each arm. “Shout loud if you need me—kitchen’s big,” he called over his shoulder. And with that, he was gone.

Squeaky Jim cleared his throat. “Big Hank probably told you I’m not a Ruby expert,” he squeaked, “but if you can get my griddle working again, I can whip up customer orders like nobody’s business.” He pulled a stack of orders from his apron pocket. “Most of them are orders for hash and today’s special, the Array of Sunshine,” he said. “Three sunny-side eggs in row! Best brunch in the kingdom.”

“Okay,” said Ruben. “We actually just helped the King with the Loop, and we had to use arrays for that. This should be a piece of cake!”

“Eggs,” corrected Squeaky Jim.

“Oh, yeah. A piece of . . . eggs,” Ruben said.

“Let’s get to work,” Scarlet said. “I’ve already got the Computing Contraption open!”

Arrays Within Arrays

“Great!” said Jim. “Since you guys know about arrays, could you create one for me now? The first order is for an Array of Sunshine; that’s just three 'sunny_side_up_egg's in a row.”

image with no caption

“Sure!” Scarlet said. “It should look something like this.” She started typing into the Computing Contraption:

>> order_one = ['sunny_side_up_egg', 'sunny_side_up_egg', 'sunny_

side_up_egg']

=> ["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"]

When Scarlet pressed ENTER, a small metal track over the griddle began to vibrate. One after another, three eggs rolled down the track, cracked against a small hammer, and dropped sunny-side-up onto the stove.

“That’s perfect!” Jim said. “But it looks like a lot of typing, and we’re gonna have a lot of orders.” His voice cracked again. “Is there any way we could do the same thing with less typing?”

“Yep!” said Scarlet. “You can also create a new array like this:

>> order_two = Array.new(3, 'sunny_side_up_egg')

=> ["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"]

“Here, we’re calling the new method on Array, which creates a list of items. The next part in the parentheses means that the array should have three items,” Scarlet explained, “and the last part means that each item should be a 'sunny_side_up_egg'. It’s the same as typing all the stuff we did for order_one.”

“I remember creating arrays on the Loop with square brackets,” Ruben said, “but I’ve never seen Array.new. What’s that do?”

“Remember how Ruby has datatypes like String?” Scarlet asked. Well, Array is another datatype. You can create an array with array literal syntax, which is just assigning a variable name to a list in square brackets. You can also create an array by calling the new method on the Arrayclass.”

“What’s a Ruby class?” Squeaky Jim asked.

“We’ll get to that in a little bit,” Scarlet said. “But the important thing is that classes are like groups of objects in Ruby, and calling the new method on the class name creates a new instance, or example, of that class.”

“Okay, that makes sense,” Ruben said. “And we can put variables in arrays, and we saw earlier that you can put strings in there. What else can go in arrays?”

“Anything!” Scarlet said. “And the items in the array don’t even have to be the same thing. Check it out!”

>> random_array = [1, 'two', 'sunny_side_up_egg', true]

=> [1, "two", "sunny_side_up_egg", true]

“A number, a string, a variable, and a Boolean, all in the same array,” Ruben said. “Neat!”

“That’s great,” Squeaky Jim said, “and the first Array of Sunshine is just about ready. But I have a feeling we’re gonna need to cook up a bunch of these—is there any way we can make an array with all of our orders in it? Sort of like a list of lists?”

“Definitely,” Ruben said, and Scarlet stepped aside so he could type:

>> order_three = ['hash']

=> ["hash"]

>> order_four = ['egg', 'hash']

=> ["egg", "hash"]

>> todays_orders = [order_one, order_two, order_three, order_four]

=> [["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"],

["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"],

["hash"], ["egg", "hash"]]

“That’s awesome! todays_orders is an array that contains four other arrays: order_one, order_two, order_three, and order_four,” Squeaky Jim said. “We’ll be done in no time. If we’ve got our orders packed up in an array, though, how do we get them back out?”

Even More Array Methods!

“There are a few things we can do,” Ruben said. “Arrays have lots of cool built-in methods we can use. For example, we can get the first item or element in an array with the first method, like this:

>> todays_orders.first

=> ["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"]

“I see,” said Scarlet. “The first method gives us the first item in the array! And while we’re talking about first, we can get the last element in an array with last!”

>> todays_orders.last

=> ['egg', 'hash']

“That’s order_four,” Jim said. “Coming right up!”

“Hang on, though,” said Ruben, and he typed quickly into the Computing Contraption:

>> todays_orders.empty?

=> false

>> todays_orders.length

=> 4

“Whoa, what’s that?” Jim asked, pushing his paper fry-cook hat back and scratching his head. “I haven’t seen empty? or length before.”

“We saw length on strings,” Scarlet said. “When we use that method on strings, it tells us how many characters the string contains. For arrays, does it tell us how many items are in the array?”

“Precisely,” said Ruben. “And we saw empty? on the Loop train stops, where it just returned a Boolean—true if the stop had no one waiting and false if there was at least one person. This empty? is for arrays, but it works the exact same way.”

Then Ruben frowned. “But there are still four orders in the list! We can get some of them with first and last, but how do we get the rest? And how do we remove them from the list as we cook them up?”

Shift! Pop! Insert!

“I think I can help with that,” Scarlet said. “We’ll need to use a couple of new array methods, though.” She reached across Ruben to the Computing Contraption and started typing:

>> todays_orders

=> [["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"],

["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"],

["hash"], ["egg", "hash"]]

>> current_order = todays_orders.shift

=> ["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"]

>> todays_orders

=> [["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"],

["hash"], ["egg", "hash"]]

“That’s perfect!” said Ruben. “How did you pull the very first order out of todays_orders and put it in the current_order variable?”

“With the shift method,” Scarlet said. “It does two things at once: it knocks the very first item off the array you call it on, and it returns, or spits out, that item!”

“So if you set a new variable equal to calling shift on an array,” the King piped up, “you basically move the item from the array to your new variable!”

Ruben and Scarlet turned to the King, who had only just finished studying the many potato sacks littering the kitchen.

“That’s . . . actually exactly right,” said Scarlet.

“Wonderful!” said the King. “But what if I want to add things onto the front of the array? Or add things onto the back? Or even—dare I say it—take things off the back?”

“Then have we got the methods for you!” Scarlet said. “I don’t want to mess up the orders, so I’ll show you on my own array called breakfast_items that I’ll make up. Take a look!” And she typed the following into the Computing Contraption:

>> breakfast_items = ['egg', 'hash', 'gravy', 'biscuit', 'sausage',

'jam']

>> current_food = breakfast_items.shift

=> egg

>> breakfast_items

=> ['hash', 'gravy', 'biscuit', 'sausage', 'jam']

>> current_food = breakfast_items.pop

=> jam

>> breakfast_items

=> ['hash', 'gravy', 'biscuit', 'sausage']

“Gadzooks!” said the King. “That’s exactly what I wanted—pop removes and returns the last item in the array, and shift does the same thing to the first item!” He watched as Scarlet typed some more.

>> breakfast_items.push('egg')

=> ['hash', 'gravy', 'biscuit', 'sausage', 'egg']

>> breakfast_items.unshift('jam')

=> ['jam', 'hash', 'gravy', 'biscuit', 'sausage', 'egg']

“Aha! I see: push adds an item to the end of the array, and unshift adds an item to the beginning of the array,” the King continued.

“Yup! Just make sure you read the array from left to right,” said Scarlet. “The first element is the one all the way on the left, and the last element is the one all the way on the right.”

“What if I want to add something to the middle?” asked the King.

Scarlet didn’t say anything, but simply typed:

>> breakfast_items

=> ['jam', 'hash', 'gravy', 'biscuit', 'sausage', 'egg']

>> breakfast_items.insert(2, 'tea')

=> ['jam', 'hash', 'tea', 'gravy', 'biscuit', 'sausage', 'egg']

“Amazing!” said Ruben. “But wait, why is tea the third item in the array?” he asked. “You called the insert method with the number 2, not the number 3!”

“This is one of the weird things about computers,” said Scarlet. “They don’t start counting at 1, like you or I do. They start at zero. If you start counting at 1, tea is in position 3, but if you start at zero, it’s one less than that. That’s why you have to tell Ruby to insert the tea element at position 2, not position 3, if you want it to be the third item.”

“I’m more confused than a bumblebee in a plastic flower factory,” the King said gloomily. “And just when I thought I was beginning to understand Ruby.”

“Hang on, I think I’ve got it,” said Squeaky Jim, his voice cracking only a little bit. “Is this right?” And he drew a diagram on the back of a hash-stained napkin:

image with no caption

“An array is like a row of boxes,” Jim said. “The first one is numbered zero, and the numbers get higher from there. You shift to take something off the front, unshift to add something to the front, push to add something to the back, and pop to take something off the back.” He looked uncertainly from Ruben to Scarlet. “Is that right?”

“That’s right!” said Ruben and Scarlet together.

“Nice work, my boy!” said the King. “You’re picking this up mighty quickly.”

“In fact,” Scarlet said, “arrays are so much like rows of boxes that you can even get an array element out by asking the array for the element by its box number! See?” She typed into the Computing Contraption:

>> breakfast_items

=> ['jam', 'hash', 'tea', 'gravy', 'biscuit', 'sausage', 'egg']

>> breakfast_items[2]

=> "tea"

“It’s like you’re telling the array exactly what box number to grab,” Ruben explained. “By saying you want breakfast_items[2], you’re telling Ruby you want the array element in slot 2, which is the third element.”

Squeaky Jim smiled. “Great!” he said. “But I wonder . . .”

“Wonder what?” asked Ruben.

“Well,” said Jim, flipping the last egg and putting it in a paper basket, “it’s fine to add and remove things from arrays, and I figure the kitchen’s software does that well enough. But what if I wanted to know something about all of the orders up front? Is there a way I could go over all of the orders and print them out one by one?”

Iterating with Arrays

“Absolutely,” said the King. “We saw that on the Loop—what was it called again?”

Iterating,” said Ruben. “It works like this!” He reached over to the Computing Contraption and began typing furiously:

>> todays_orders.each do |order|

>> puts "#{order}"

>> end

["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"]

["hash"]

["egg", "hash"]

“Yes, that does look familiar!” said the King. “And that will print out each order in the todays_orders array?”

“You got it,” said Scarlet. “But remember, there’s a way of writing it with less code than the do/end block.” She quickly typed:

>> todays_orders.each { |order| puts "#{order}" }

["sunny_side_up_egg", "sunny_side_up_egg", "sunny_side_up_egg"]

["hash"]

["egg", "hash"]

“That’s right!” said the King. “You can use the curly brackets instead of do/end when there’s just one line of code in the block.” He scratched his bushy white beard. “Though I’m still a little mystified by these blocks.”

“We’ll talk more about them soon!” said Scarlet. “For now, we should make sure we’re all set with arrays and customer orders here in the Hashery.”

Squeaky Jim nodded. “I think I get the hang of arrays okay, and we’re caught up on orders for the time being,” he said, tossing the last order ticket into the trash. “But all this talk about iterating has me wondering if there isn’t another problem we can solve.”

“What’s that?” said Scarlet.

“Well,” squeaked Jim, “Big Hank and I have been trying to figure out how best to print out the Hashery menu for our customers. Do you think iterating over an array might be a good way to do it?”

“What’s the menu made up of?” asked Ruben.

“In Ruby terms, just strings and numbers,” Jim said. “Each string would be an item on the menu, and every item would have a number representing the price. I figure since we can mix strings and numbers in arrays, that might make sense.”

“Hmm,” said Scarlet. “I don’t think so. How would you pair up the menu items and their prices? Even if you just alternated them, you might mess it up with all the pushing, popping, shifting, and unshifting you’d be doing whenever the menu changed.”

“You have a point,” Jim admitted. “Well, maybe an array of arrays? Each array element could be its own little array, and every little array could just contain a menu item name and its price.”

“That’s a little better,” Ruben said. “At least then your menu names and prices would be together. What do you think, Scarlet?”

Scarlet thought for a moment. “No,” she finally said. “I think instead of an array, we want to use a hash.”

Hash in the Hashery

I’m sure you’re thinking to yourself right now: “Okay, we’re in the Hashery. Hash is served. Surely this idea that Ruby has a built-in thing called a hash is a big joke, right?”

image with no caption

Well, it’s not. It’s zero percent joke. Hashes are one of the coolest parts of Ruby, so while Scarlet, the King, Ruben, and Squeaky Jim sort out the differences between breakfast hash and Ruby hashes, I’ll take a second to explain them to you.

Arrays are like rows of boxes, right? Each element has its own numbered slot to live in, like items on a grocery list. This is great so long as all the stuff on each line of the list—that is, every element in the array—keeps to itself and does its own thing. But what if you want to show that two elements are somehow related?

Think of a dictionary: in a dictionary, you have a word and its definition. Unlike with a grocery list, you wouldn’t say that the words are all on their own lines and the definitions are all on their own lines, since that leaves out the biggest part of the dictionary: the connections between words and their meanings. Squeaky Jim’s orders are like a list, and no order really affects any other order, so an array makes sense. But for his menu, where he’s got to associate menu items with their prices, he needs something more like a dictionary. And for that, Ruby uses hashes.

Hashes are easier to show than tell (isn’t everything?), so check out the following code. It pairs up our heroes (along with Squeaky Jim and Big Hank) with their descriptions. Go ahead and type it into IRB, and notice how we’re using curly brackets ({}) instead of square brackets ([]) as we did with arrays:

>> our_heroes = {

>> :the_king => 'the ruler of the kingdom',

>> :ruben => 'a Ruby wizard in training',

>> :scarlet => 'a Ruby wizard in training',

>> :big_hank => 'the owner of the Hashery',

>> :squeaky_jim => 'a fry cook at the Hashery'

>> }

This code takes a variable, our_heroes, and stores a hash in it. Don’t be confused by the curly brackets—this isn’t a block! Hashes aren’t commands; they’re just a bunch of what we call key-value pairs. A word and its definition are a good example of a key-value pair: the word is the key, and the word’s definition is the value. Just as with a dictionary, you use a hash key to look up a hash value.

Each key-value pair is separated from the next one by a comma, which makes them a bit like arrays. The similarities don’t end there! For example, if you had the preceding hash, you could type:

>> our_heroes[:the_king]

and you’d get:

=> "the ruler of the kingdom"

This is a lot like looking up array values, only instead of providing the element number inside the square brackets, you write the hash key.

Don’t worry that the hash keys look weird right now; those things that look like variables with colons in front of them are called symbols, and we’ll get to them in the next chapter.

Just as with arrays, you can create a hash with literal syntax or with the new method. These two lines of code are “saying” the same thing:

>> hashery_menu = {}

=> {}

>> hashery_menu = Hash.new

=> {}

Sometimes you’ll see an alternate way of writing hashes. Instead of using the little hash rockets (=>), some people put the colons after the symbol names, which would make the our_heroes hash look like this:

>> our_heroes = {

>> the_king: 'the ruler of the kingdom',

>> ruben: 'a Ruby wizard in training',

>> scarlet: 'a Ruby wizard in training',

>> big_hank: 'the owner of the Hashery',

>> squeaky_jim: 'a fry cook at the Hashery'

>> }

=> {:the_king=>"the ruler of the kingdom", :ruben=>"a Ruby wizard in

training", :scarlet=>"a Ruby wizard in training", :big_hank=>"the

owner of the Hashery", :squeaky_jim=>"a fry cook at the Hashery"}

Both examples are totally correct Ruby, and you should pick whichever one is easier for you to remember. (I like the colons, since they’re faster to type.)

Finally, there are a few neat methods you can call on hashes to get the keys, values, or key-value combinations out of them. For instance, calling the keys method on your hash will give you an array of its keys:

>> our_heroes.keys

=> [:the_king, :ruben, :scarlet, :big_hank, :squeaky_jim]

You can also call the values method on a hash to get an array of its values:

>> our_heroes.values

=> ['the ruler of the kingdom', 'a Ruby wizard in training', 'a Ruby

wizard in training', 'the owner of the Hashery', 'a fry cook at the

Hashery']

There are a few more hash methods worth knowing about. Just as empty? tells you if an array is empty, it also tells you if a hash has no key-value pairs:

>> our_heroes.empty?

=> false

>> empty_hash = {}

>> empty_hash.empty?

=> true

You can also use length to find out how many sets of pairs are in your hash:

>> our_heroes.length

=> 5

And last but not least, you can use some brand-new hash methods, has_key? and has_value?, to check whether a hash contains a certain key or value:

>> our_heroes.has_key?(:ruben)

=> true

>> our_heroes.has_key?(:trady_blix)

=> false

>> our_heroes.has_value?('a fry cook at the Hashery')

=> true

However, you’re probably demanding to know: “How can I get all the keys and values of my hash together?” Well, the best way to do that is to iterate over the hash. This looks a whole lot like iterating over an array—in fact, there’s only one tiny difference!

>> our_heroes.each do |hero, role|

>> puts "#{hero} is #{role}."

>> end

Go ahead and try it out. (Make sure you use double quotes for your puts—remember, you need that if you’re going to put variables in your string.) Did you spot that tiny difference I mentioned? You need both hero and role between the pipe characters (||) in your block. We had just one variable between the pipes for arrays, but hashes have keys and values, so we need to tell the Ruby block about both. If all goes well, you’ll get a list of all of your intrepid heroes and their stations in life:

the_king is the ruler of the kingdom.

ruben is a Ruby wizard in training.

scarlet is a Ruby wizard in training.

big_hank is the owner of the Hashery.

squeaky_jim is a fry cook at the Hashery.

=> {:the_king=>"the ruler of the kingdom", :ruben=>"a Ruby wizard in

training", :scarlet=>"a Ruby wizard in training", :big_hank=>"the

owner of the Hashery", :squeaky_jim=>"a fry cook at the Hashery"}

Speaking of our heroes, it sounds like Scarlet and Ruben have finished explaining hashes to the King and Squeaky Jim. (I have very acute hearing.) Let’s see if they’ve figured out how to use hashes to iterate over the Hashery menu.

Rollicking Ranges

“I’ve got just the idea for iterating over the Hashery menu,” said Squeaky Jim, and this time his voice didn’t crack at all. “All we need to do is—”

At that very moment, Big Hank came lumbering over from the far side of the kitchen.

“I hate to interrupt,” he boomed, “but I’m having a heck of a time with the range. In fact, there’s only one little Ruby detail I need to get it working, but I’ll be a monkey’s tax attorney if I can figure it out. Mind giving me a hand?”

“Sure thing!” said Scarlet. “What’s the trouble?”

“Follow me,” said Big Hank, and they crossed the enormous kitchen, past counters piled high with eggs, flour, potatoes, and other ingredients, past ovens and spatulas and those little forks with only three prongs, until they reached the gleaming new range on the far side of the kitchen.

“Ain’t she a beauty?” Big Hank asked. “Only wish I could figure her out. Here’s where I’m stuck.” He pointed to the glowing IRB >> prompt on the range’s console. It said:

>> current_temperature = (300..400)[0]

NoMethodError: undefined method `[]' for 300..400:Range

“I overheard what you were saying about arrays,” Hank said, “and the instruction manual for the range says it’ll go from 300 to 400 degrees. So I figured I could use the square brackets to get the temperature in position zero, which should be 300.”

“Oh, I see the problem,” said Ruben. “This range doesn’t use an array for the temperature! It uses a range.”

“A range?” Big Hank said.

“That’s Ruby’s way of giving you a bunch of different values right next to each other,” Ruben said. “Ranges don’t do all of the things arrays can do, but we can make them into arrays pretty easily. Check it out!” He started typing:

>> ('a'..'f').to_a

=> ["a", "b", "c", "d", "e", "f"]

>> ('a'...'f').to_a

=> ["a", "b", "c", "d", "e"]

>> (1..9).to_a

=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

>> (1...9).to_a

=> [1, 2, 3, 4, 5, 6, 7, 8]

>> (1..9).first

=> 1

>> (1..9).last

=> 9

“I’ll be a Christmas goose!” Hank bellowed. “That’s amazing! But I’ve got a couple of questions. First, what’s that to_a bit do?”

“The to_a method turns ranges into arrays,” Scarlet said. “Since range values are all right next to each other, Ruby can figure out what the array should look like. See? It works on letters of the alphabet and numbers!”

“Not only that,” Ruben added, “but once the range is an array, you can iterate over it just like any array.”

“I see,” Hank said, twirling his mustache. “But answer me this: why d’you get some ranges with two dots and some with three?”

“That’s just how you tell Ruby whether or not to include the last thing in the range,” Ruben said. “Two dots means ‘include the first thing, everything up to the last thing, and the last thing between the parentheses in the range,’ and three dots means ‘include the first thing and everything up to, but not including, the last thing between the parentheses in the range.’”

“That sounds a bit confusing,” Jim squeaked.

“It can be,” Scarlet admitted. “That’s why I usually stick to the two-dot ranges. It makes more sense to have both numbers in the range.”

“Got it,” Big Hank said. “Last question. If I want the first thing in the range, I can convert it to an array with to_a and just grab the first element with [0]. But can I also use this first method you just showed me?”

“Of course!” Ruben said, and typed:

>> current_temperature = (300..400).first

=> 300

With a pleasant beep, the range quickly heated up to 300 degrees. The smell of fresh hash began to waft through the air.

“You’ve done it! I can’t thank you kids enough.” Big Hank laughed, slinging hash across the range like a gleeful diner cowboy. “I can’t help but feel a little silly, though. It was such a small thing!”

“It always feels like that with programming,” Scarlet said. “But the more you do it, the more you realize it’s always some small thing, and you get much better at fixing things quickly.”

“Speaking of quick,” Big Hank said, “the lunch rush’ll be here any minute.” He surveyed the kitchen, which was full of ungrated potatoes and unfried eggs. “What do you say—want to grab a quick bite, then maybe give me a hand?” He smiled, and his great black mustache bounced on his face. “Of course, food’s on the house. Anything for the King and his friends!”

Ruben and Scarlet looked at each other, then at the King. The King nodded. “We’ve come all this way,” he said. “We might as well stick around a bit longer!”

Order Up!

Now that the Hashery is back at 100 percent, Big Hank and Squeaky Jim (who, now that he’s more confident with Ruby, squeaks much less) need your help to get that menu ready for the customers. Jim didn’t get a chance to tell us his plan, but I’m pretty sure you’ve got this one. Easy as pie . . . uh, eggs, right?

Let’s begin by making a new file called hashery_menu.rb. (Peek back to Chapter 1 if you don’t remember how to do this, or ask your local adult for help.) Then open your file and type the following code.

hashery_menu.rb

hashery_menu = {

eggs: 2,

hash: 3,

jam: 1,

sausage: 2,

biscuit: (1..3)

}

hashery_menu.keys.each do |item|

puts "Today we're serving: #{item}!"

end

hashery_menu.each do |item, price|

puts "We've got #{item} for $#{price}. What a deal!"

end

puts "Here's what a biscuit'll run ya, depending on how much butter

you want:"

hashery_menu[:biscuit].to_a.each do |price|

puts "$#{price}"

end

This is what you and Jim would have put together a little bit ago, so there’s nothing new or scary here!

This is the output you’ll see when you run the code using the command ruby hashery_menu.rb:

Today we're serving: eggs!

Today we're serving: hash!

Today we're serving: jam!

Today we're serving: sausage!

Today we're serving: biscuit!

We've got eggs for $2. What a deal!

We've got hash for $3. What a deal!

We've got jam for $1. What a deal!

We've got sausage for $2. What a deal!

We've got biscuit for $1..3. What a deal!

Here's what a biscuit'll run ya, depending on how much butter you

want:

$1

$2

$3

There are a couple of new combinations of ideas, though, so let’s step through them one by one and see how they work. Take a look:

hashery_menu = {

eggs: 2,

hash: 3,

jam: 1,

sausage: 2,

biscuit: (1..3)

}

Here, we’re just creating a hash called hashery_menu. It’s got keys like :eggs and :hash, and each key is paired with a value, like 2 for :eggs and 3 for :hash. This is how much we’ll charge for the item on our menu.

Next, we have this bit:

hashery_menu.keys.each do |item|

puts "Today we're serving: #{item}!"

end

We’re using the keys method to get a list of all the keys in our hash, then giving that list (or array) to the each method. For each key, we’re printing out the string: Today we're serving: #{item}! So, for example, when we get to the key :eggs, we’ll print out: Today we're serving: eggs! The code loops over each item in our hash and then puts each menu item to the screen.

hashery_menu.each do |item, price|

puts "We've got #{item} for $#{price}. What a deal!"

end

Things are getting a bit trickier here. When we call the each method on the hash itself, we give the do block both the hash key (item) and the value of that key (price). For example, when we get to :eggs (which is paired with the value 2), we’ll print out: We've got eggs for $2. What a deal!

puts "Here's what a biscuit'll run ya, depending on how much butter

you want:"

hashery_menu[:biscuit].to_a.each do |price|

puts "$#{price}"

end

Finally, we’ll work a little Ruby magic with the :biscuit in our hashery_menu. First, we access its value with hashery_menu[:biscuit]. Then, since that value is a range, we can call the to_a method on it to make it an array, then use each just as we did before to go through all its items. We’ll print out our message saying what a biscuit will cost, and then the do block will print out the possible prices: $1, $2, and $3, each on its own line.

You can test out your entire menu program by entering ruby hashery_menu.rb at the command line, and it should look like the output I showed you just a moment ago.

You’ve got a solid menu going here, but if you want to make Squeaky Jim and Big Hank absolutely weep with joy, try the following ideas on for size.

Your menu has a pretty sweet range in it, and you even convert it to an array! I don’t see any regular arrays in your menu, though, and you’re completely allowed to have arrays as hash values. Why not add a :random_special key (for the Special of the Day) with an array of prices as the value? If I told you that you could call the sample method on an array to get Ruby to spit out a random element from an array, how might you use it here?

You could get really fancy and shift, unshift, push, or pop values onto or off of your :random_special array. Looking at the code you’ve already got, how would you call these methods on the array value of your :random_special key?

Speaking of the push method, there’s a cool shortcut for it in Ruby. It’s called the shovel operator, and it works like this. The line with the << is exactly the same as bagel_types.push('cinnamon raisin'):

>> bagel_types = ['plain', 'sesame', 'everything']

=> ["plain", "sesame", "everything"]

>> bagel_types << 'cinnamon raisin'

=> ["plain", "sesame", "everything", "cinnamon raisin"]

Try replacing your pushes with <<s, and then read more about the shovel operator at http://www.ruby-doc.org/. Hint: How could it help you build strings in Ruby?

You Know This!

You might feel like your brain is overflowing with all the new array, hash, and range magic we learned, but don’t worry—we’ll go over everything once more to make sure you’ve got it all.

Let’s start with arrays, which are just lists of information. You have two ways of creating arrays. You can use array literal syntax, using square brackets like this:

>> breakfast = ['chunky bacon, 'chunky bacon', 'chunky bacon']

=> ["chunky bacon", "chunky bacon", "chunky bacon"]

Or you can use Array.new to do the same thing:

>> breakfast = Array.new(3, 'chunky bacon']

=> ["chunky bacon", "chunky bacon", "chunky bacon"]

You found out that arrays can contain anything, including strings, numbers, variables, Booleans, and even other arrays.

You learned a whole bunch of array methods, including:

§ empty?, which returns true if an array has no items and false if it has at least one item

§ length or size, which do the same thing—return the number of items in an array

§ first, which returns the first element in an array without removing it

§ last, which returns the last element in an array without removing it

§ shift, which returns the first element in an array and removes it from the array

§ unshift, which adds elements to the front of the array

§ push, which adds elements to the back of the array

§ pop, which removes and returns the last element of the array

§ insert, which can add an element anywhere in the array

Whew!

Let’s practice a bit in IRB with some more examples to refresh your memory:

>> empty_array = []

=> []

>> empty_array.empty?

=> true

>> not_empty_array = [1, 2, 3, 4, 'I declare a thumb war']

=> [1, 2, 3, 4, "I declare a thumb war"]

>> not_empty_array.empty?

=> false

>> not_empty_array.length

=> 5

>> not_empty_array.first

=> 1

>> not_empty_array.last

=> 'I declare a thumb war'

>> not_empty_array

=> [1, 2, 3, 4, 'I declare a thumb war']

>> first_item = not_empty_array.shift

=> 1

>> not_empty_array

=> [2, 3, 4, 'I declare a thumb war']

>> not_empty_array.unshift(first_item)

=> [1, 2, 3, 4, 'I declare a thumb war']

>> last_item = not_empty_array.pop

=> 'I declare a thumb war'

>> not_empty_array

=> [1, 2, 3, 4]

# We could also do not_empty_array << last_item

>> not_empty_array.push(last_item)

=> [1, 2, 3, 4, 'I declare a thumb war']

# Insert the number 5 at position 4; remember, arrays start counting

at 0!

>> not_empty_array.insert(4, 5)

=> [1, 2, 3, 4, 5, 'I declare a thumb war']

We talked about how to access arrays using square brackets:

>> junk_drawer = ['lightbulb', 'dead battery', 'some pens', 'old

penny']

=> ["lightbulb", "dead battery", "some pens", "old penny"]

>> junk_drawer[2]

=> "some pens"

Finally, we reviewed how to iterate over an array:

>> junk_drawer.each do |thing|

>> puts thing

>> end

lightbulb

dead battery

some pens

old penny

=> ["lightbulb", "dead battery", "some pens", "old penny"]

which is exactly the same as:

>> junk_drawer.each { |thing| puts thing }

lightbulb

dead battery

some pens

old penny

=> ["lightbulb", "dead battery", "some pens", "old penny"]

Next up: hashes. Hashes are different from arrays because they aren’t just lists. Instead, they associate keys with values (think: dictionary word with definition). Just like arrays, however, hashes can be created with literal syntax or the new method:

>> hashery_menu = {}

=> {}

>> hashery_menu = Hash.new

=> {}

And, just as with arrays, you can access hash values with square brackets:

>> hashery_menu = {

>> eggs: 2,

>> hash: 3,

>> jam: 1,

>> sausage: 2,

>> biscuit: (1..3)

>> }

=> {:eggs=>2, :hash=>3, :jam=>1, :sausage=>2, :biscuit=>1..3}

>> hashery_menu[:jam]

=> 1

We saw a few hash methods, including empty? (which returns true if a hash has no key-value pairs and false if it has at least one pair), length (which returns the number of pairs in a hash), keys (which returns an array of the keys in the hash), values (which returns an array of the values in the hash), has_key? (which returns true if the hash includes a particular key and false otherwise), and has_value? (which returns true if the hash includes a particular value and false otherwise).

Here they are again in all their glory:

>> hashery_menu.empty?

=> false

>> empty_hash = {}

=> {}

>> empty_hash.empty?

=> true

>> hashery_menu.length

=> 5

>> hashery_menu.keys

=> [:eggs, :hash, :jam, :sausage, :biscuit]

>> hashery_menu.values

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

>> hashery_menu.has_key?(:jam)

=> true

>> hashery_menu.has_key?(:zebra)

=> false

>> hashery_menu.has_value?(3)

=> true

>> hashery_menu.has_value?(42)

=> false

We also learned that we could iterate over a hash just like we can iterate over an array, only we need to put variables for the key and the value between the pipes in our code block:

>> hashery_menu.each do |item, price|

>> puts "#{item} costs #{price}"

>> end

eggs costs 2

hash costs 3

jam costs 1

sausage costs 2

biscuit costs 1..3

=> {:eggs=>2, :hash=>3, :jam=>1, :sausage=>2, :biscuit=>1..3}

Last (but not least), we covered ranges. Ranges are just a bunch of Ruby values that happen to be next to each other. We saw that two dots inside the parentheses included both ends of the range, while three dots included the first end but only up to (not including) the second one:

>> (1..5).to_a

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

>> (1...5).to_a

=> [1, 2, 3, 4]

We also learned a few range methods, including to_a (which turns a range into an array), first (which returns the first item in the range), and last (which returns the last item in the range):

>> ('a'..'c').to_a

=> ["a", "b", "c"]

>> ('a'..'c').first

=> "a"

>> ('a'..'c').last

=> "c"

All right! We made it. Great work so far—but don’t get too cocky. That lunch rush is coming, and the next chapter’s gonna get a little crazy.