Saturday, April 23, 2016

Always study the standard libraries



I was faced with a common problem: given an [a-z] string, find out the most common letter inside it and the number of times it appears.

My first solution was very simple, indeed:

def most_common_letter(string)
    h = Hash.new
    characters = string.chars
    characters.each do |c|
        if (h[c].nil?) then
            h[c] = 1
        else
            h[c] = h[c]+1
        end
    end
    maxk = nil
    maxc = -1
    h.each do |k,v|
        if (v > maxc) then
            maxk = k
            maxc = v
        end
    end
    [maxk , maxc]
end

It works, but it is too big and we may do some changes to make it smaller and a bit more efficient.

def most_common_letter(string)
    h = Hash.new
    string.chars.sort.map { |c|
        h[c] = 0 if (h[c].nil?)
        h[c] = h[c] + 1
    }
    maxk = nil
    maxc = -1
    h.each do |k,v|
        if (v > maxc) then
            maxk = k
            maxc = v
        end
    end
    [maxk , maxc]
end

As you may see, here I omitted the unnecessary variable characters and applied all transformations directly from string.

Besides, I used an if construction to initialize all elements the hash h with 0 when they are created. h[c] = 0 if (h[c].nil?) created h[c] and initializes it with 0 if the hash has no such key as c. With this we replaced five lines with only two.

A better solution, of course, but still a bit big for Ruby standards. Now let's see what happens when we really use Ruby standard libraries.

def most_common_letter(string)
    counts = Hash.new(0)
    string.each_char {|c| counts[c] += 1 }
    counts.max_by{|k,v| v}
end

Here we take advantage of the fact Hash provides for a default value. When I say counts = Hash.new(0), I'm telling Ruby to initialize all new elements of the hash counts, whatever the key, with 0. Then we use the each_char block to populate the hash counts, whose keys are the letters and the values are the counts of the corresponding letters. Finally we use the max_by method, which counts inherits from Enumerable, to return the key => value pair with the highest count.

As you may see, just by knowing how to use the default libraries one may simplify the code a lot, making it more efficient too. A great reason to read Ruby's documentation carefully and apply what you learn there.