Tag Archives: Ruby Tuesday

Ruby Tuesday – Date#leap?

Today’s Ruby Tuesday is on Date#leap?.

Date#leap? operates on a Date object, and returns true if the year is a leap year in the Gregorian calender, or false if not.

Date.new(2015, 1, 4).leap?
=> false
Date.new(2012, 1, 1).leap?
# => true
Date.new(2017, 1, 1).leap?
# => false
Date.new(2000, 1, 1).leap?
# => true
Date.new(1900, 1, 1).leap?
# => false

By using Date#leap?, you don’t have to code the rules to check if a year is a leap year, or even have to double check the rules about when the century is a leap year.

There is also a class level version Date::leap?, that will take a integer value for the year, and return a true or false value.

Date.leap? 2015
# => false
Date.leap? 2012
# => true
Date.leap? 2017
# => false
Date.leap? 2000
# => true
Date.leap? 1900
# => false

Both the instance and class level versions of ‘leap?` can handle Year Zero, and negative years.

Date.leap? -1
# => false
Date.leap? -4
# => true
Date.leap? 0
# => true
Date.new(-4, 1, 1).leap?
# => true

I wouldn’t know if that is valid myself, but that is why it is useful to have this a built in method on Date instead of having to code it up myself.

–Proctor

Ruby Tuesday – Date#-

Today’s Ruby Tuesday is on Date#-.

Date#- returns the difference between two dates if passed a Date object. The difference is expressed as a rational number, as this method is also available on DateTime objects as well.

Date.new(2015, 4, 21) - Date.new(1999, 12, 31)
# => (5590/1)
Date.new(2015, 4, 21) - Date.new(1999, 4, 20)
# => (5845/1)
Date.new(2015, 4, 21) - Date.new(2015, 4, 20)
# => (1/1)
Date.new(2015, 4, 21) - Date.new(2015, 4, 23)
# => (-2/1)

If a numeric value is passed to Date#- it returns a Date object that is that many days of a difference.

Date.new(2015, 4, 21) - 1
# => #<Date: 2015-04-20 ((2457133j,0s,0n),+0s,2299161j)>
Date.new(2015, 4, 21) - 365
# => #<Date: 2014-04-21 ((2456769j,0s,0n),+0s,2299161j)>
Date.new(2015, 4, 21) - (1/2)
# => #<Date: 2015-04-21 ((2457134j,0s,0n),+0s,2299161j)>
Date.new(2015, 4, 21) - (2/2)
# => #<Date: 2015-04-20 ((2457133j,0s,0n),+0s,2299161j)>

Date#- will not only accept positive numeric values, but also negative values, and will result with a day that is in the future.

Date.new(2015, 4, 21) - -1
# => #<Date: 2015-04-22 ((2457135j,0s,0n),+0s,2299161j)>

–Proctor

Ruby Tuesday – Date#jd

Today’s Ruby Tuesday covers Date#jd.

Date#jd returns the number of days since the beginning of the Julian calendar.

require 'date'
# => true

Date.new.jd
# => 0
Date.new(2015, 04, 14).jd
# => 2457127
Date.new(0, 1, 1).jd
# => 1721058

If you notice above, we get 1721058 for the days since the start of the Julian calendar. One thing to notice is that we can pass an optional “Julian day number which denotes the day of calendar reform” to Date#new. If we pass in the constant Date::GREGORIAN to new, and then call Date#jd on the result, we get a different value coming back for the number of days since the start of the Julian calendar, 1721060 vs 1721058.

Date.new(0, 1, 1, Date::GREGORIAN).jd
=> 1721060

While Ruby does not have a corresponding method for getting the number of days since the start of the Gregorian calendar, we can do a difference between the number of Julian days for a date and the number of Julian days for the date 0000-01-01. But be warned, as we just saw above, to get an accurate number, we need to specify that we want 0000-01-01 to be the Gregorian version of that date.

Date.new(2015, 04, 14).jd - Date.new(0, 1, 1).jd
# => 736069
Date.new(2015, 04, 14).jd - Date.new(0, 1, 1, Date::GREGORIAN).jd
# => 736067
Date.new(2015, 04, 14, Date::GREGORIAN).jd - Date.new(0, 1, 1, Date::GREGORIAN).jd
# => 736067

If we just construct a new Date, and do not specify and Julian start date, we notice that we can see the number of Julian days in the constructor, and if we specify we want the date to be Gregorian calendar based, we can see the number of Julian days is different there as well.

Date.new(0, 1, 1)
# => #<Date: 0000-01-01 ((1721058j,0s,0n),+0s,2299161j)>
Date.new(0, 1, 1, Date::GREGORIAN)
# => #<Date: 0000-01-01 ((1721060j,0s,0n),+0s,-Infj)>

Hope this shined as little bit of knowledge on Ruby Dates for you as it did me.

–Proctor

Ruby Tuesday – Date#wday

Today’s Ruby Tuesday is on Date#wday.

Date#wday returns an integer value for the day of the week that the day falls on.

Date.new(2015, 4, 7).wday
# => 2
Date.new(1970, 1, 1).wday
# => 4
Date.new(1, 1, 1).wday
# => 6

The values that Date#wday can return will be between 0-6, with 0 being Sunday.

Date.new(2015, 4, 5).wday
# => 0
Date.new(2015, 4, 5).sunday?
# => true
Date.new(2015, 4, 11).wday
# => 6
Date.new(2015, 4, 11).saturday?
# => true

–Proctor

Ruby Tuesday – Array#delete

Today’s Ruby Tuesday covers Array#delete.

Array#delete removes all items in the array that are equal to the object passed as an argument to the method.

list = [1, 2, 1, 3, 1, 5]
# => [1, 2, 1, 3, 1, 5]
list.delete(1)
# => 1
list
# => [2, 3, 5]
list.delete(3)
# => 3
list
# => [2, 5]

mississippi = "Mississippi".chars
# => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"]
mississippi.delete("s")
# => "s"
mississippi
# => ["M", "i", "i", "i", "p", "p", "i"]

Array#delete by default returns nil if the item was not found in the list.

no_fours = [1, 2, 3, 5, 6, 7]
# => [1, 2, 3, 5, 6, 7]
no_fours.delete(4)
# => nil

But if you decide to use Array#delete to remove nils from the array, you may not be able to check that it was removed because nil is returned as the value that was removed.

contains_nil = [1, "a", nil, :foo]
# => [1, "a", nil, :foo]
contains_nil.delete(nil)
# => nil
contains_nil
# => [1, "a", :foo]

If you come across that scenario, Array#delete allows you to specify a block that gets called if the item was not found in the array.

contains_nil2 = [1, "a", nil, :foo]
# => [1, "a", nil, :foo]
contains_nil2.delete(nil) {:bar}
# => nil
contains_nil2
# => [1, "a", :foo]
contains_nil2.delete(nil) {:bar}
# => :bar

As mentioned above, Array#delete removes all items that are equal to the argument passed in. The catch is Ruby has three different equality comparisons: ==, eql?, and equal?. So let’s try a couple of different scenarios to see which equality operator Ruby uses when doing an equality check.

mississippi = "Mississippi".chars
# => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"]
mississippi.delete("s")
# => "s"
"s" == "s"
# => true
"s".eql? "s"
# => true
"s".equal? "s"
# => false

So it looks like it is not equal?, but we still have == and eql? left that it could be, so let’s try another scenario.

integers = [1, 2, 3, 4]
# => [1, 2, 3, 4]
integers.delete(1.0)
# => 1
integers
# => [2, 3, 4]
1 == 1.0
# => true
1.eql? 1.0
# => false
1.equal? 1.0
# => false

As we can see here, 1 was removed when 1.0 was passed as an argument, so we see that Array#delete uses the == equality check to determine if the items are equal.

–Proctor

Ruby Tuesday – String#to_sym

Today’s Ruby Tuesday covers String#to_sym.

String#to_sym operates on a String object, and turns the string into a Symbol.

Why would someone want to have a Symbol instead of a String?

The main reason is that Symbols are created once for the lifetime of a program, and use the same reference. Every reference to the symbol :foo refers to the same instance of :foo, and it doesn’t matter when, or where, in the program lifetime it is.

Strings on the other hand are unique objects, so two strings of the same value are not necessarily the same object, and depends on when, where, and how they were created.

"foo".to_sym
# => :foo
"Foo".to_sym
# => :Foo
"foo".__id__ == "foo".__id__
# => false
:foo.__id__ == :foo.__id__
# => true

One case where this might be useful is where you have to tokenize strings, and are going to want to use them as keys for a map. Think header columns in a CSV file, but please don’t write your own CSV parser, as Ruby has a nice one built in.

"firstName,lastName,preferred name,age".split(",").map(&:to_sym)
# => [:firstName, :lastName, :"preferred name", :age]

The other place where you might want to use String#to_sym is when you are trying to create Symbols that cannot easily be created by just using the normal symbol notation.

":foo".to_sym
# => :":foo"
"peanut butter".to_sym
# => :"peanut butter"

–Proctor

Ruby Tuesday – Set#disjoint?

Today’s Ruby Tuesday is on Set#disjoint?.

To start using Sets, we first need to require Set.

require 'set'

Set#disjoint? is useful when you want to make sure a group of items is not in another group of items.

Without using Set#disjoint? you would probably be writing something like the following:

[1, 3, 1, 5, 7].none? { |item| [2, 4, 6, 8].include? item }
# => true

While this works, it does not make the intent of what you are trying to do explicit, which is where Set#disjoint? comes in.

Set#disjoint? operates against a set, and takes another Set as it’s argument, and checks to see if there is no common element between the two sets.

[1, 3, 1, 5, 7].to_set.disjoint? [2, 4, 6, 8].to_set
# => true
[1, 2, 3].to_set.disjoint? [1, 3, 5, 7].to_set
# => false

If a nil is present in both sets, Set#disjoint? treats the nils as equal and returns false.

[2, nil].to_set.disjoint? [1, 3, 5, 7, nil].to_set
=> false

Set#disjoint? works against empty sets as well.

[].to_set.disjoint? [1, 2, 3].to_set
# => true
[1, 2, 3].to_set.disjoint? [].to_set
# => true

If two empty sets are passed to Set#disjoint?, it returns true, as both sets have no elements, and therefore no elements in common.

[].to_set.disjoint? [].to_set
# => true

–Proctor

Ruby Tuesday – Enumerable#flat_map

Today’s Ruby Tuesday is on Enumerable#flat_map.

Enumerable#flat_map became this week’s topic, because yesterday I was doing some exercises in Elixir, and was thinking I could use the flat_map that is defined in Elixir to solve the solution, but it wasn’t working the way I thought it should.

I was getting confused with why it was failing, and I went to check my assumptions of how flatmap works in Erlang, and it behaved the same way. Which, after seeing it, is unsurprising given their common runtime, but I couldn’t figure out where my mental model of flat_map was coming from, other than the name alone.

It was time to pull up a Ruby REPL, and see if Ruby was behaving the way I thought it “should” be behaving.

[1, [[2], 3]].flat_map {|x| x * x}
# TypeError: no implicit conversion of Array into Integer
# from (pry):11:in `*'

No! It failed in Ruby as well.

I had myself convinced that flat_map was the equivalent of doing a Array#flatten, and then calling Enumerable#map on the result.

[1, [[2], 3]].flatten.map {|x| x * x}
# [1, 4, 9]

This is the equivalent of what I was trying to get, and flat_map wasn’t working for me, and obviously, it is not the way Enumerable#flat_map behaves either.

After looking deeper, I realized that somehow, I had the application of the conceptual flatten and map methods swapped.

The way Enumerable#flat_map actually behaves is as if you called map on the Enumerable, and then called flatten on the results.

[[:a, 1], [:b, 2], [:c, 3]].flat_map {|item, count| Array.new(count, item) }
# [:a, :b, :b, :c, :c, :c]

[[:a, 1], [:b, 2], [:c, 3]].map {|item, count| Array.new(count, item) }
# [[:a], [:b, :b], [:c, :c, :c]]
[[:a, 1], [:b, 2], [:c, 3]].map {|item, count| Array.new(count, item) }.flatten
# [:a, :b, :b, :c, :c, :c]

And if an Enumerable is not returned by the map block passed to Enumerable#flat_map, it behaves the same as if you just called Enumerable#map.

[1, 2, 3].flat_map {|x| x * x}
# [1, 4, 9]
[1, 2, 3].map {|x| x * x}
# [1, 4, 9]

Here is hoping that this saves you some confusion as well.
–Proctor

Ruby Tuesday – Time#utc

In honor of the time change this weekend, today’s Ruby Tuesday is on Time#utc.

In the past few years I have had to deal with numerous bugs crop up around the changes between Daylight time and Standard time. In honor of all the time conversion issues I have dealt with in the past couple of years, I thought it would be timely, pun absolutely intended, to explore time conversions. As as this deals with local time vs UTC time conversions, let me preface this with a warning:

Here there be dragons.

With that said, let us see if we can channel the spirit of Dirk the Daring, and attempt to face down some of those dragons.

For this upcoming time change we will be moving the clocks forward an hour at 2 AM.

Let’s see what time it is right before the time change.

Time.new(2015,3,8, 1,59,59).utc
# 2015-03-08 07:59:59 UTC

And when 2 AM “arrives”, we move the clock forward and it becomes 3 AM.

Time.new(2015,3,8, 3,00,00).utc
# 2015-03-08 08:00:00 UTC

So far, all that looks good. But what happens at 2 AM?

Time.new(2015,3,8, 2,00,00).utc
# 2015-03-08 08:00:00 UTC

Though 2 AM on March 8th of 2015 never really happens, at least here in the Dallas/Fort Worth area, we do get a valid time returned when we call Time#utc, and the result is the same as when we called the utc method for 3AM.

That was interesting; as far if that is good or bad, that depends on how well your system can handle that behavior.

Now onto what happens later in the year when we set our clocks back an hour.

The first time that 2 AM rolls around we set our clocks back an hour to 1 AM, so let’s see what time 12:59:59 AM is in UTC, since we know that only happens once.

Time.new(2015,11,1, 00,59,59).utc
# 2015-11-01 05:59:59 UTC

And we only hit 2 AM only once as well, since the first time we came to 2 AM we all skipped it and relived the 1 AM hour.

Time.new(2015,11,1, 2,00,00).utc
# 2015-11-01 08:00:00 UTC

So now the question is: “What time is it at 1 AM?”

Time.new(2015,11,1, 1,00,00).utc
# 2015-11-01 07:00:00 UTC

Hrmm…..

It looks like we only get the time for 1 AM for the second time it rolls around, as it returns 7 AM UTC, and we have completely lost the 6 AM UTC hour. Dragons!

Hopefully you can avoid most of these issues and just work in UTC or epoch seconds across your systems. But as our systems continue to interact with more and more outside systems, the likelihood of having to deal with these issues increase, so please be aware.

–Proctor

Ruby Tuesday – Hash#merge

Today’s Ruby Tuesday covers Hash#merge.

Hash#merge takes a hash as it’s argument, and returns a new hash with the combined items from both hashes.

{a: 1, b: 2, c: 3}.merge({d: 1, e: 2, f: 3})
# {:a=>1, :b=>2, :c=>3, :d=>1, :e=>2, :f=>3}

Hash#merge has the ability to take a block which allows you to specify behavior of what you would like to happen when key collisions are encountered.

{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1}) {|key, val1, val2| val1 * val2 }
# {:a=>4, :b=>2, :c=>3, :d=>8, :s=>3, :f=>1}
{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1}) {|key, val1, val2| [val1, val2] }
# {:a=>[1, 4], :b=>2, :c=>3, :d=>[4, 2], :s=>3, :f=>1}

If a key is present in both hashes and no block is given, Hash#merge takes the value for the key from the hash that is passed into the merge method. This behaves as if you passed a block that returned the value for the second hash.

{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1})
# {:a=>4, :b=>2, :c=>3, :d=>2, :s=>3, :f=>1}
{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1}) {|key, val1, val2| val2 }
# {:a=>4, :b=>2, :c=>3, :d=>2, :s=>3, :f=>1}

If you would rather have the value from the hash that merge is being called on in the case of a key collision, you can provide a block that returns the value from the first hash.

{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1})
# {:a=>4, :b=>2, :c=>3, :d=>2, :s=>3, :f=>1}
{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1}) {|key, val1, val2| val1 }
# {:a=>1, :b=>2, :c=>3, :d=>4, :s=>3, :f=>1}

Hash#merge is especially useful if you take in an options hash of values because you don’t want to force the consumer of the method to have to specify every single property that someone could possibly want.

By using Hash#merge, your method can have a hash that represents the defaults, which you merge with the options that they passed in to get a complete list of all of the possible options.

defaults = {happy: true, hungry: false, sleepy: false}
# {:happy=>true, :hungry=>false, :sleepy=>false}
method_args = {hungry: true}
# {:hungry=>true}
settings = defaults.merge(method_args)
# {:happy=>true, :hungry=>true, :sleepy=>false}

–Proctor