Yesterday I attended a talk about truth tables. It was based on the premise of an interview question and whether the candidates were able to model and represent in code the appropriate truth table. The problem question was along the lines of "write a method that receives the month and the year and outputs how many days there are in that month".

Sounds easy, doesn't it? We all know which months have 30 and which 31 days in them. Apart from February, that is. February usually has 28 days, except that in leap years it has 29. How do we know which years are leap years? There are certain rules that allow us to determine leap years:

To sum it up, a year is a leap year when

Sounds easy, doesn't it? We all know which months have 30 and which 31 days in them. Apart from February, that is. February usually has 28 days, except that in leap years it has 29. How do we know which years are leap years? There are certain rules that allow us to determine leap years:

- The year is evenly divisible by 4;
- If the year can be evenly divided by 100, it is NOT a leap year, unless;
- The year is also evenly divisible by 400. Then it is a leap year

To sum it up, a year is a leap year when

- it is evenly divided by 4 but NOT evenly divided by 100.
- It is evenly divided by 4, 100 AND 400

Month | Year | No of Days |
---|---|---|

jan, mar, may, jul, aug, oct, dec. | any | 31 |

apr, jun, sep, nov | any | 30 |

feb | year % 4 != 0 | 28 |

feb | (year % 4 == 0) && (year % 100 != 0) | 29 |

feb | (year % 4 == 0) && (year % 100 == 0) && (year % 400 != 0) | 28 |

feb | (year % 4 == 0) && (year % 100 == 0) && (year % 400 == 0) | 29 |

The speaker used PHP code for a working solution, using multi-conditional statements . Now we could use the same approach in Ruby with a multi-branch conditional or maybe a

Knowing all this, we can write our method as follows:

def month_days(year, month)

h = {

%w(jan mar may jul aug oct dec) => 31,

%w(apr jun sep nov) => 30,

%w(feb) => ((year % 4 == 0) && (year % 400 == 0)) ||

((year % 4 == 0) && (year % 100 != 0)) ?

29 : 28

}

h.select {|k,v| k.include? month}.values

end

$> puts month_days 1900, 'feb'

28

$> puts month_days 2000, 'feb'

29

$> puts month_days 1900, 'sep'

30

We use Arrays for the Hash keys and we use the ternary operator as a value for the

Maybe it's just me getting more finicky in my old age or it's because I just spent a few hours reading though some horrible JavaScript code, but this looks and feels more readable and a bit more 'je-ne-sais-quoi' than a big If or Case statement. Of course, Ruby being Ruby, there'll be a better way, so if you know of any feel free to share it with me by commenting below.

*Case*statement and that would work just as well. But, this being Ruby, there's always another way. We can leverage two powerful Ruby features to model our truth table as a Hash:- Everything is an expression in Ruby. I mean everything, and that includes Hash keys and values. Every statement gets evaluated into an object and that's a beautiful thing.
- Ruby is great for List Comprehensions . I suppose that's because of its Lisp influences but the fact is Ruby offers great ways of making lists out of lists.

Knowing all this, we can write our method as follows:

def month_days(year, month)

h = {

%w(jan mar may jul aug oct dec) => 31,

%w(apr jun sep nov) => 30,

%w(feb) => ((year % 4 == 0) && (year % 400 == 0)) ||

((year % 4 == 0) && (year % 100 != 0)) ?

29 : 28

}

h.select {|k,v| k.include? month}.values

end

$> puts month_days 1900, 'feb'

28

$> puts month_days 2000, 'feb'

29

$> puts month_days 1900, 'sep'

30

We use Arrays for the Hash keys and we use the ternary operator as a value for the

*february*key. Our returning object then, is the value of a Hash key that is generated by filtering the original Hash's keys (Arrays) based on the desired month. Beautiful.Maybe it's just me getting more finicky in my old age or it's because I just spent a few hours reading though some horrible JavaScript code, but this looks and feels more readable and a bit more 'je-ne-sais-quoi' than a big If or Case statement. Of course, Ruby being Ruby, there'll be a better way, so if you know of any feel free to share it with me by commenting below.