Ruby Tips & Tricks: Part 2

Last week, I posted part one of my Ruby Tips & Tricks for people new to Ruby. In part two, we've got a whole new set of fantastic things you may find useful:

Working with Arrays

As you probably know, Arrays are essentially lists of objects. Once you've got an array containing items, you often need to perform actions on those items.

Firstly a simple but powerful one, we'll take a look at the uniq method. This allows you to return an array which only contains items which only exist once.

array = [1,2,2,2,3,4,5,5,6]  
array.uniq      => [1,2,3,4,5,6]  

Next, the flatten method. This is used to turn an array of arrays into a single flat array containing all items of all nested arrays. Quite the mouthful!

array = [1, [2,3,[4,5,6], 7, 8], 9]  
array.flatten      #=> [1,2,3,4,5,6,7,8,9]  

Finally, compact is a method which allows you to remove nils from an array. This may not sound terribly useful but it comes in handy every now and then and is worth having in your repertoire.

array = [1,2,nil,3,nil,nil,nil]  
array.compact    #=> [1,2,3]  

Operator Methods on Classes

If you're making your own classes, you may be interested to learn how easy to create your own operator methods for your instances.

ten = Money.new(10.00)  
twenty = Money.new(20.00)

ten + twenty       #=> Money(30.00)  
twenty - ten       #=> Money(10.00)  
twenty / ten       #=> Money(2.00)  
ten * twenty       #=> Money(200.00)  

It's really simple to do this, you simply define methods using the name of the operator you want to use.

class Money

  attr_accessor :decimal

  def initialize(decimal)
    @decimal = decimal
  end

  def +(other)
    Money.new(@decimal + other.decimal)
  end

  def -(other)
    Money.new(@decimal - other.decimal)
  end

end  

Before we move on, there's also a [] method which can be useful in certain circumstances. In this example, we're demonstrating it in its most basic form:

class Config

  def initialize(config = {})
    @config = config
  end

  def [](value)
    @config[value]
  end
end

config = Config.new(:host => 'potato.com')  
config[:host]        #=> 'potato.com'  

Mapping / Collecting

Using map allows you to manipulate items in an Enumerable object (for example, an array or hash) and return a new array containing your new objects.

# In this example, we've multiplied each number by two
array = [1,2,3,4,5]  
array.map { |i| i * 2} #=> [2,4,6,8,10]

# Next, we'll work with some strings and add some text
array = ["Adam", "Jim", "Bob"]  
hellos = array.map do |name|  
  "Hello #{name}"
end  
hellos   #=> ["Hello Adam", "Hello Jim", "Hello Bob"]  

Reducing / Injecting

In this example, we're going to use inject to add together all the items in an array. The basic premise for inject is that it will loop over all the items, run your code and set the resulting value to a variable which is provided to each future iteration.

numbers = [1,2,3,4,5,6]  
total = numbers.inject(0) do |total, number|  
  total + number
end  

The value we pass to the inject method is the starting point for the total variable in our block. You can see here, we simply return the total plus the current number. Finally, when all items have been evaluated, it will return the total (in this case, 21).

Inject can also be used with a hash if you wish to manipulate the contents of a hash and return a new Hash. In this code, we're going to change the keys of the hash to be symbols rather than strings.

hash = {'name' => 'Adam', 'age' => 28, 'country' => 'UK'}  
hash.inject(Hash.new) do |new_hash, (old_key, old_value)|  
  new_hash[old_key.to_sym] = old_value
  new_hash
end  

Just remember to return new_hash when you've set the value. As before, whatever you return from your block will be used as new_hash in the future iterations. If you don't return new_hash, it will simply return the value of old_value.

Extracting data from a string

Using regular expressions, we can easily extract certain bits of information from a string. We just need to create a regular expression with a capturing group around the item(s) we want to extract.

Our string is Name: Adam and we want to extract the person's name from it.

if match = string.match(/\w+\: (\w+)/)  
  match[1]          #=> 'Adam'

  # Return the values of all items which were captured.
  match.captures    #=> ["Adam"]

  # Return the beginning & end position of any capturing groups in the regexp. 
  match.begin(1)    #=> 6
  match.end(1)      #=> 10
end  

That's about it.

... and that's about it for my Ruby Tips & Tricks. I'm planning to write a post next week about my favourite gems, how they work and why I love them.

If there's anything else you'd like me to cover, do feel free to drop me a message on Twitter and I'll happily include it.