Showing posts with label data structures. Show all posts
Showing posts with label data structures. Show all posts

Friday, September 4, 2015

Indifferent Access in Ruby Hashes (with ActiveSupport)

I kept seeing this “Hash with indifferent access” in open source projects, but never knew quite what it was. Turns out it’s something I use in Rails all of the time without knowing, particularly in dealing with params. It allows me to access values in a hash using either string or symbolized keys.

For example:

params['name'] == params[:name]
# => true

However, a normal Hash in Ruby does not allow this:

hash = { 'name' => 'Fred' }
hash['name']
# => 'Fred'
hash[:name]
# => nil
hash['name'] == hash[:name]
# => false

We can fix this by making this into a hash with indifferent access:

new_hash = hash.with_indifferent_access
new_hash['name'] == new_hash[:name]
# => true

The hash can also be defined/initialized with indifferent access, either by calling that method on the initial hash assignment, or by creating it using ActiveSupport (which is what the with_indifferent_access method does under the hood anyway):

hash = ActiveSupport::HashWithIndifferentAccess.new(name: 'Fred')
hash['name'] == hash[:name]
# => true

Note: When working in straight Ruby, this will not work as it’s using ActiveSupport. One would likely need to run gem install active_support and require it properly.

Source: apidock and RoR api

Wednesday, July 22, 2015

Check if array contains any element of another array

I wanted to check if there were common elements in two separate arrays. Seems simple enough, but I was trying to find a way to do this without looping over both arrays in some kind each jungle:

wizard_of_oz_characters = ['tin man', 'scarecrow', 'lion']
batman_villains = ['scarecrow', 'joker', 'bane']
wizard_of_oz_characters.each do |character|
  batman_villains.each do |villain|
    return false if character == villain
  end
end

I was able to pare it down to something simpler:

wizard_of_oz_characters.any? {|character| batman_villains.include?(character) }

…but at it’s core, it’s still looping through and checking each record in sequence. Not what I was after.

Then I found a StackOverflow answer which had exactly what I wanted:

(wizard_of_oz_characters & batman_villains).empty?

This returns the intersection of the two arrays (['scarecrow']), then asks if it is empty. Perfect! And the syntax is kinda fun too.

But what is it actually doing? One of the comments on the SO answer said this is faster because it’s linear rather than quadratic, and although it has been a few years since high school Geometry, I still know quadratic is much more intensive than linear. However, I wanted to find out more.

Turns out, the & operator is treating the Arrays like Sets, an entirely different data type in Ruby. From the Ruby docs themselves, a Set is a “hybrid of Array’s intuitive inter-operation facilities and Hash’s fast lookup.”

So you get the syntax of an Array with the speed of a Hash. And you get this fun and terse syntax. I’m on board with that.