What Are the Benefits of the New Hash Syntax in Ruby 1.9

What are the benefits of the new hash syntax in Ruby 1.9?

It just looks nicer--it's syntactic sugar; it ends up being the same thing.

When mixing keys (ew, why would you do that?) I use the old hash-rocket syntax for the entire hash.

With symbol values I also use the old hash-rocket syntax for the entire hash–this looks icky:

{ ohai: :kthxbye }

I don't like mixing the two styles in the same hash–I think it's confusing.

This is all based on personal preference, though.

Allowing for Ruby 1.9's hash syntax?

Even in Ruby < 1.9, you could use symbols for keys. For example:

# Ruby 1.8.7
settings = { :host => "localhost" }
puts settings[:host] #outputs localhost
settings.keys[0].class # => Symbol

Ruby 1.9 changes the way that you create hashes. It takes the key and converts it to a symbol for you, while eliminating the need for a hash rocket.

# Ruby 1.9.2
settings = { host: "localhost" }
settings[:host] # => "localhost"
settings.keys[0].class # => Symbol

In both cases, if I try to access settings[:name] with settings["name"], I'm going to get nil. All Ruby 1.9 does is allow for a new way of creating hashes. To answer your question, you cannot, as far as I know, use the new {key: value} syntax if you want backwards compatibility with Ruby 1.8.

Is there a difference between this: value and this = value in Rails?

When the key: value syntax is used in a Hash literal, the key always becomes a Symbol:

{foo: "bar"}.keys[0].class # => Symbol

When the key => value syntax is used, the key can be any type (including a Symbol):

{:foo => "bar"}.keys[0].class # => Symbol
{1 => "bar"}.keys[0].class # => Fixnum

Are there benefits to returning a tapped hash over an immediate hash?

Using tap, you could use conditionals (or more complex logic):

def some_method
{}.tap do |data|
data[:foo] = something if condition
data[:bar] = something_else unless condition
end
end

But even in this case, I'd probably avoid tap and write:

def some_method
data = {}
data[:foo] = something if condition
data[:bar] = something_else unless condition
data
end

Hash syntax strangeness

They are completely equivalent, except the first can only be used since ruby 1.9 (and higher, of course).

In ruby 1.8 the hash syntax used the =>, also known as the hash rocket. You could put anything in front, and anything behind, but the thing in front is your key, behind the value. If you have a symbol as key, and a symbol as value, you would write:

:method => :delete

But you could also write

{ 1 => 'one', :2 => 'two', 'THREE' => 3 }

Now, for ruby 1.9.x, a new shorter syntax was introduced. Since most people use symbols as keys, you can now write:

method: :delete

Which is just a shorter/cleaner version. Also note that it is possible to mix both styles, which in some cases is needed.

E.g. in ruby 1.8 you would write:

{ :class => 'smthg', :'data-type' => 'a type' }

This would translate to the following in ruby 1.9

{ class: 'smthg', :'data-type' => 'a type' }

Note that you can still keep using the "old" hash syntax as well. It is a matter of preference. Personally for hashes with only symbols as keys, I use the clean/short version. I generally try not to mix hash-style in a single hash :)

Difference between `:provider = ' value '` and `provider: 'value'`

There is no difference.

nitz@comp:~$ irb
irb(main):001:0> {a:1}
=> {:a=>1}

This is a new syntax for specifying hashes with keys that are symbols, which is the "normal" way (as far as I can see) of defining hashes.

Also see What are the benefits of the new hash syntax in Ruby 1.9?

Ruby on Rails: how we should write symbol - symbol: vs :symbol =

To help you decide which hash literal syntax to use, check out:

  • Why using the hash rocket syntax is still sometimes necessary.

  • Ruby Style Guide which the Rails Style Guide adheres to.

Snippet from the Ruby Style Guide on Collections:

Preferably use symbols instead of strings as hash keys.

# bad
hash = { 'one' => 1, 'two' => 2, 'three' => 3 }

# good
hash = { one: 1, two: 2, three: 3 }

Use the Ruby 1.9 hash syntax when your hash keys are symbols.

# bad
hash = { :one => 1, :two => 2, :three => 3 }

# good
hash = { one: 1, two: 2, three: 3 }

Don't mix the Ruby 1.9 hash syntax with hash rockets in the same hash literal.

When you've got keys that are not symbols stick to the hash rockets syntax.

# bad
{ a: 1, 'b' => 2 }

# good
{ :a => 1, 'b' => 2 }

Why use symbols as hash keys in Ruby?

TL;DR:

Using symbols not only saves time when doing comparisons, but also saves memory, because they are only stored once.

Ruby Symbols are immutable (can't be changed), which makes looking something up much easier

Short(ish) answer:

Using symbols not only saves time when doing comparisons, but also saves memory, because they are only stored once.

Symbols in Ruby are basically "immutable strings" .. that means that they can not be changed, and it implies that the same symbol when referenced many times throughout your source code, is always stored as the same entity, e.g. has the same object id.

Strings on the other hand are mutable, they can be changed anytime. This implies that Ruby needs to store each string you mention throughout your source code in it's separate entity, e.g. if you have a string "name" multiple times mentioned in your source code, Ruby needs to store these all in separate String objects, because they might change later on (that's the nature of a Ruby string).

If you use a string as a Hash key, Ruby needs to evaluate the string and look at it's contents (and compute a hash function on that) and compare the result against the (hashed) values of the keys which are already stored in the Hash.

If you use a symbol as a Hash key, it's implicit that it's immutable, so Ruby can basically just do a comparison of the (hash function of the) object-id against the (hashed) object-ids of keys which are already stored in the Hash. (much faster)

Downside:
Each symbol consumes a slot in the Ruby interpreter's symbol-table, which is never released.
Symbols are never garbage-collected.
So a corner-case is when you have a large number of symbols (e.g. auto-generated ones). In that case you should evaluate how this affects the size of your Ruby interpreter.

Notes:

If you do string comparisons, Ruby can compare symbols just by comparing their object ids, without having to evaluate them. That's much faster than comparing strings, which need to be evaluated.

If you access a hash, Ruby always applies a hash-function to compute a "hash-key" from whatever key you use. You can imagine something like an MD5-hash. And then Ruby compares those "hashed keys" against each other.

Every time you use a string in your code, a new instance is created - string creation is slower than referencing a symbol.

Starting with Ruby 2.1, when you use frozen strings, Ruby will use the same string object. This avoids having to create new copies of the same string, and they are stored in a space that is garbage collected.

Long answers:

https://web.archive.org/web/20180709094450/http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings

http://www.randomhacks.net.s3-website-us-east-1.amazonaws.com/2007/01/20/13-ways-of-looking-at-a-ruby-symbol/

https://www.rubyguides.com/2016/01/ruby-mutability/

Passing hashes instead of method parameters

Both approaches have their own advantages and disadvantages, when you use an options hash replacing standard arguments you lose clarity in the code defining the method but gain clarity whenever you use the method because of the pseudo-named paramaters created by using an options hash.

My general rule is if you either have a lot of arguments for a method (more than 3 or 4) or lots of optional arguments then use an options hash otherwise use standard arguments. However when using an options hash it is important to always include a comment with the method definition describing the possible arguments.

When is it better to use a Struct rather than a Hash in Ruby?

Personally I use a struct in cases when I want to make a piece of data act like a collection of data instead of loosely coupled under a Hash.

For instance I've made a script that downloads videos from Youtube and in there I've a struct to represent a Video and to test whether all data is in place:


Video = Struct.new(:title, :video_id, :id) do
def to_s
"http://youtube.com/get_video.php?t=#{id}&video_id=#{video_id}&fmt=18"
end

def empty?
@title.nil? and @video_id.nil? and @id.nil?
end
end

Later on in my code I've a loop that goes through all rows in the videos source HTML-page until empty? doesn't return true.

Another example I've seen is James Edward Gray IIs configuration class which uses OpenStruct to easily add configuration variables loaded from an external file:

#!/usr/bin/env ruby -wKU

require "ostruct"

module Config
module_function

def load_config_file(path)
eval <<-END_CONFIG
config = OpenStruct.new
#{File.read(path)}
config
END_CONFIG
end
end

# configuration_file.rb
config.db = File.join(ENV['HOME'], '.cool-program.db')
config.user = ENV['USER']

# Usage:
Config = Config.load_config('configuration_file.rb')
Config.db # => /home/ba/.cool-program.db
Config.user # => ba
Config.non_existant # => Nil

The difference between Struct and OpenStruct is that Struct only responds to the attributes that you've set, OpenStruct responds to any attribute set - but those with no value set will return Nil



Related Topics



Leave a reply



Submit