Koren Leslie Cohen

  • About
  • Blog
  • Contact

11 comments

Ruby / Rails

Nested Arrays, Hashes & Loops in Ruby

June 9, 2014 by Koren Leslie Cohen

devise redirects

Arrays and hashes are common data types used to store information.  The main difference between an array and a hash is the manner in which data is stored.  Arrays, represented by square brackets, contain elements which are indexed beginning at 0.  So if an array contained three elements, the indexes for those elements would be array[0], array[1] and array[2].  Hashes, represented by curly brackets, store information in key/value pairs.  Instead of checking array[0] to get the value at a particular index, you would check hash[key] to get the value at that index.

Nested Arrays

A nested array is exactly as it sounds – an array with one or more arrays nested inside of it.   Nested arrays are useful when storing things in multiple categories.  There can be multiple levels of nesting.

Two levels:

nested_array = [[“hello”, “world”], [“love”, “ruby”]]

Three levels:

more_nested_array = [[“hello”, [“world”, “new york”]], [“love”, “ruby”]]

Accessing Values in a Nested Array

If we want to access the first value in nested_array, we would do so in the same manner as a regular array: nested_array[0].  This would return the first element in nested_array, which is also an array: [“hello”, “world”]. If we want to dip into the inner arrays, we need to add an extra index for the inner array.  For example, nested_array[0][1] would return the second element of the first nested array: “world”.

Looping Through A Nested Array

If we were to iterate through the outer array in more_nested_array, only the first layer inside the array would be included in the loop.  For example:

more_nested_array.each do |element|
  element << "test"
end

Produces the following output:

[[“hello”, [“world”, “new york”], “test”], [“love”, “ruby”, “test”]]

As you can see, our loop iterated through the first layer of more_nested_array and added the string “test” to each element.  Since the first layer in more_nested_array consisted of two arrays, it added “test” as an additional element of each nested array.

If we want to iterate through the inner elements, we have to add an inner loop:

more_nested_array.each do |element|
  element.each do |inner_element|
    inner_element << "test"
  end
end

The output for the above code is quite different:

[[“hellotest”, [“world”, “new york”, “test”]], [“lovetest”, “rubytest”]]

As you can see, this looped through the outer array and then looped through the second level  of the array and added the string “test” to each element at the second level, which consisted of three strings and an array.  It skipped the third layer (i.e. it did not append “test” to “world” or “new york”) because we only included two nested loops.  The number of nested loops we need will correspond to the number of layers we wish to access.  If we wanted to loop through just the third layer, we would use a third nested loop:

more_nested_array.each do |element|
  element.each do |inner_element|
    if inner_element.is_a?(Array)
      inner_element.each do |third_layer_element|
        third_layer_element << "test" 
      end
    end
  end
end

This appended the string “test” only to the third layer elements:

[[“hello”, [“worldtest”, “new yorktest”]], [“love”, “ruby”]]

With nested loops, you can control exactly which elements of a nested array you’re accessing.  If we wanted to add “test” to the end of every string at every level, we could do the following:

more_nested_array.each do |element|
  element.each do |inner_element|
    if inner_element.is_a?(Array)
      inner_element.each do |third_layer_element|
        third_layer_element << "test"
       end 
    else 
      inner_element << "test"
    end
  end
end

Notice this skips the first layer and modifies only the second and third level, or inner_element and third_layer_element, to add “test” to each string inside more_nested_array:

[[“hellotest”, [“worldtest”, “new yorktest”]], [“lovetest”, “rubytest”]]

Nested Hashes

Nested hashes can be a bit more complicated.  As stated above, a hash is a container that holds key and value pairs, noted with a hash rocket =>.  A simple example of this is a dictionary.  Dictionary would be the hash, each word would be a key, and the definition would be its corresponding value.  The syntax looks something like this:

dictionary = {
  :hello => "greeting",
  :apple => "fruit"
}

In programming, we rarely use such a simple example.  Often, we’ll encounter nested hashes, which also contain arrays.  For example, say we wanted to store our holiday supplies by holiday and by season, we may end up with something that looks more like this:

holiday_supplies = {
  "winter" => { 
    "Christmas" => ["lights", "tree"],
    "New Years" => "champagne glasses"
  },
  "summer" => {
    "July Fourth" => ["BBQ", "flags"]
  },
  "spring" => {
    "Memorial Day" => "BBQ"
  },
  "fall" => {
    "Labor Day" => "hot dogs"
  }
}

As you can see, there is a hash called holiday_supplies, and four nested hashes representing the seasons.  There are also two key/value pairs in the above example: (1) season => {holiday => supplies}, and (2) holiday => supplies.

Accessing Keys and Values in Nested Hashes

To access information in a standard hash, we could check the hash[key] to get the value at that key.  In the dictionary example, if we entered dictionary[:hello] we would get the value “greeting”.

Accessing keys and values in a nested hash works the same way.  If we wanted to know the value of winter (or what holidays/supplies to anticipate), we would enter holiday_supplies[“winter”], except instead of returning a single value, this returns another key/value pair, since “winter” is also a hash:

{“Christmas”=>[“lights”, “tree”], “New Years”=>”champagne_glasses”}

If we wanted to find out which supplies we need for a specific holiday, things change a bit.  Instead of entering one key, we need to enter two keys:

holiday_supplies[“winter”][“Christmas”]

This dives another level into the hash and returns the value of the second key/value pair, holiday => supplies: [“lights”, “tree”].

Looping Through a Nested Hash

The best way to understand a nested loop is this: the first iteration loops through the outer hash and its key/value pair, and the second iteration loops through the inner hash and its key/value pair.  Just like we had to add a second level to our above index to access the innermost values in the hash, we likewise need to add a second loop to access and manipulate the innermost values.

Say we wanted to print each of the supplies we need for all seasons to prepare for the upcoming year, we would want to use nested loops:

supplies = []

holiday_supplies.each do |key, value|
  value.each do |key2, value2|
    supplies << value2
  end
end

puts supplies.flatten.uniq

This would give us a nice list each supply and would remove duplicates from the list:

lights
tree
champagne_glasses
BBQ
flags

If we used a single loop, we would not get the same output:

supplies = []

holiday_supplies.each do |key, value|
  supplies << value
end

puts supplies.flatten.uniq

This prints for us the values of the outer hash only:

{"christmas"=>["lights", "tree"], "new_years"=>"champagne_glasses"}
{"july_fourth"=>["BBQ", "flags"]}
{"memorial_day"=>"BBQ"}
{"labor_day"=>"hot dogs"}

This isn’t as useful, as “BBQ” is listed twice, forcing us to do extra work to remove it from the list.  If we’re particularly forgetful (or literal as computers are), we may purchase multiple BBQs.  We probably don’t want to purchase multiple BBQs, so having a clean list which eliminates duplicates is important.  Inner values; inner loop.

Another example for the use of nested loops would be for comparison purposes.  Say we have a tweet that is over 140 characters and we need to shorten some of the words to make it fit.  We have a list of common words and their abbreviations in a hash, and we need to figure out how to loop through the tweet and replace any of the matching words.

Here is our hash:

 replacements = {
    "hello" => "hi",
    "to" => "2",
    "two" => "2",
    "too" => "2",
    "for" => "4",
    "four" => "4",
    "be" => "b",
    "you" => "u",
    "at" => "@", 
    "and" => "&"
  }

Although the tweet itself is a string, we can .split the tweet and create an array containing each word in the tweet.  Since we’re accessing two different data types (an array and a hash), we want to use nested loops:

tweet_array = tweet.split(" ")
tweet_array.map do |word|
  replacements.each do |key, value|
    word = value if word.downcase == key
  end
  word
end.join(" ")

The above code iterates through the tweet array and for each word in the array it iterates through the hash and replaces any word in the tweet array if the word matches a hash key.  In other words, if a word in the tweet matches a keyword in the hash, it is replaced by that keyword’s value, or the word’s shortened version.

  • About
  • Latest Posts
Connect
Koren Leslie Cohen
Product manager at Facebook. Former senior product manager at Dollar Shave Club in Los Angeles and software engineer at J.Crew / Madewell in New York City. Recovering trial lawyer.
Connect
Latest posts by Koren Leslie Cohen (see all)
  • PM Career Story - April 28, 2022
  • How to Transition into Product Management - December 26, 2017
  • What I’ve Learned in My First Few Months as a Product Manager - October 14, 2015

Related Posts

Rails: Rename a Database/Table Column
Ruby Classes

Share

Facebook Google+ Twitter Pinterest Email

Comments Cancel reply

Your email address will not be published. Required fields are marked *

*

code

  1. Shrikant says

    February 27, 2015 at 6:47 pm

    Thanks a ton for this article.
    I am having some issues with my nested hashes. COuld you please guide me?

    I have hash of common_id as follows:

    { “signin” =>

    “element1” =>

    { “ios_id => “i1”, “android_id” => “a1” }

    “element2” =>

    { “ios_id => “i2”, “android_id” => “a2” }

    }

    trying to build a hash here on runtime, which will put all the related ids as a value to elements.

    ids[“signin”][“elment1”] => “i1”

    How can I convert the ids hash to get above result.

    Reply
  2. Francisco says

    October 28, 2015 at 11:18 am

    Pretty useful information. Just ran into a hash of arrays and this post helped me decide the way to iterate it. Thanks.

    Reply
  3. JOSE says

    December 3, 2016 at 4:33 pm

    Hey really helpful and clear – thanks a lot!

    Reply
  4. Hilliard M Scott says

    December 4, 2017 at 3:34 pm

    Thanks for this article, great explanations!.
    I haven’t made it through the whole article, but I think your holiday_supplies example should have ‘christmas’ capitalized ‘Christmas’

    Reply
    • Koren Leslie Cohen says

      December 26, 2017 at 5:11 pm

      Thanks for the feedback – I’ve updated.

      Reply
  5. Hilliard M Scott says

    December 4, 2017 at 3:57 pm

    In your single loop holiday_supplies.each needs a ‘do’ statement
    and there are only two BBQ’s in the list instead of three, because “Labor Day” => “hot dogs”

    And I guess the ‘tweet’ string is missing..

    Reply
    • Koren Leslie Cohen says

      December 26, 2017 at 5:12 pm

      Thanks for catching that. Not sure where the third BBQ came from!

      Reply
  6. Majid says

    April 5, 2018 at 2:10 pm

    for the tweet, if you put hello workd in the beginning , it does not convert it to hi.

    Reply
  7. DailyWalk365 says

    November 3, 2018 at 11:24 am

    This has to be one of the best and most easy to follow explanation of nested arrays and hashes I’ve come across. Following your examples and using them in https://repl.it/ I was able to better understand how they work and how to iterate through them. Also, thanks for leaving suggested reading at the bottom. That was very thoughtful. To us new programmers this is a very helpful article.

    Thank you

    Reply
  8. Daniel toma says

    December 19, 2018 at 5:52 pm

    Are there situations in which you prefer to use .collect in lieu of .each ?

    Reply
  9. CASEY C HANNAN says

    February 20, 2019 at 6:50 pm

    This is the best explanation / example i’ve found! Thanks for this.

    Reply

Back to Blog

  • GitHub
  • Instagram
  • LinkedIn
  • RSS
  • Twitter

Looking for something?

Copyright 2023 Koren Leslie Cohen