webdevjeff.us

Web Developer Jeff George

Blog

Similar != same

A look at Ruby hashes and JavaScript objects

Oct. 26, 2015

The web development immersive at Dev Bootcamp focuses on Ruby as its primary back-end language, so students here spend a lot of time switching between Ruby and JavaScript, the front-end language that's built in to all modern web browsers. We can ignore the essentially religious debate about which langauge is "better," but we can't avoid the fact that they're different. One of the most important differences between Ruby and JavaScript is in how they handle the composite data structures. Ruby does this with the hash, while JS calls its version an object.

Ruby's hash and JavaScript's object do roughly the same thing—they both represent a collection of values accessed using keys. Their usage is similar, as is their syntax, but they have some important differences under the hood. In this blog post, I'll look at first the similarities, then the differences between the two.

Somewhat alike...

Ruby's hash and JavaScript's object look alike. Their syntax is very similar. Let's look at how you might create a hash and an object with literal notation:

  • # Ruby hash:
  • tim = { name: "Tim", age: 25, shoe_size: 9 }
  • // JS object:
  • var tim = { name: "Tim", age: 25, shoe_size: 9 };

You also access their values in similar, but not identical, ways:

  • # Ruby hash:
  • tim[:age]
  • => 25
  • // JS object:
  • tim.age;
  • => 25

In each case, the data structure consists of a set of labelled values; Tim's age (label) is 25 (value). In Ruby, we call the label a key, and in JavaScript, we call it a property, but the net effect is the same. It's interesting and sometimes useful to note that in Ruby, the keys can be of any data type (string, symbol, number, etc.), while in JavaScript, the properties are not really any type of data at all—they're just properties of the object. In our example hash, we created all of the keys as symbols.

The values in Ruby hashes and JavaScript objects may be of any type—numbers, strings, arrays, even hashes or objects. For example, we can add an array to our two Tims simply by accessing a new key or property, and assigning it a value:

  • # Ruby hash:
  • tim[:pets] = [ "dog", "possum", "iguana" ]
  • => [ "dog", "possum", "iguana" ]
  • tim[:pets][1]
  • => "possum"
  • // JS object:
  • tim.pets = [ "dog", "possum", "iguana" ];
  • => [ "dog", "possum", "iguana" ];
  • tim.pets[1]
  • => "possum"

...but different somehow

One important difference advantage JavaScript objects have over Ruby hashes is that a JS object can take a function as a value. Since methods in Ruby aren't objects, you can't assign a method as a hash value. In order to make our tim object greet us in JavaScript, we add a property that has a function as its value:

  • // JS object:
  • tim.say_hi = function(name) { console.log("Hi there, " + name + "!") };
  • => function (name) { console.log("Hi there, " + name + "!") }
  • tim.say_hi("Bob")
  • => Hi there, Bob!

This is a handy bit of functionality possessed by JavaScript objects, with no direct equivalent for Ruby hashes. You could use a Proc object in a Ruby hash, but it's more likely that if you want to put a method in a data structure in Ruby, you really need something more robust than a simple hash. For these purposes, Ruby provides classes, which carry with them a whole suite of capabilities including inheritance of built-in methods from superclasses, the potential to add class methods inherited by all objects belonging to the class, and protected instance variables. JavaScript's rough equivalent to Ruby's class is the constructor function, which streamlines the creation of similar objects, but otherwise lacks the power and versatility of Ruby's class.

In the case the Ruby version of our friend tim, as he becomes more complex, we need to consider creating a Person class, and creating tim as an instance of that class, instead of as a simple hash. But that's a subject for another post, which would compare Ruby classes with JavaScript constructor functions.