Tag Archives: Ruby

Ruby Tuesday – Array#compact

Today’s Ruby Tuesday in on Array#compact.

Array#compact operates against a given array and returns a new array with any nils removed.

array1 = [1, 2, nil, 3, nil, nil, 4, nil]
# => [1, 2, nil, 3, nil, nil, 4, nil]
array2 = array1.compact
# => [1, 2, 3, 4]
array1
# => [1, 2, nil, 3, nil, nil, 4, nil]
array2
# => [1, 2, 3, 4]
Array.new(7)
# => [nil, nil, nil, nil, nil, nil, nil]
Array.new(7).compact
# => []
[].compact
# => []
array_with_no_nils = [1, 2, 3, 4, 5]
# => [1, 2, 3, 4, 5]
compacted_array_with_no_nils = array_with_no_nils.compact
# => [1, 2, 3, 4, 5]
compacted_array_with_no_nils
# => [1, 2, 3, 4, 5]
array_with_no_nils
# => [1, 2, 3, 4, 5]

As seen in the examples above, Array#compact preserves the original order of the elements in the source array.

Array#compact only performs a shallow removal of any nils, and does not remove nils from any nested arrays.

mixed_type_array = [nil, :a, nil, nil, "b", [1, 2, nil, 3], nil, :c]
# => [nil, :a, nil, nil, "b", [1, 2, nil, 3], nil, :c]
mixed_type_array.compact
# => [:a, "b", [1, 2, nil, 3], :c]

And because nils in Ruby are falsey, we can do a quick sanity check about if Array#compact is looking at nils specifically, or falsey items in general, by creating a new Array filled with falses and calling compact on it.

Array.new(9){false}.compact
=> [false, false, false, false, false, false, false, false, false]

The Array class also defines Array#compact!, which modifies the underlying state of the Array it is being called on.

array3 = array1.dup
# => [1, 2, nil, 3, nil, nil, 4, nil]
array4 = array3.compact!
# => [1, 2, 3, 4]
array3
# => [1, 2, 3, 4]
array4
# => [1, 2, 3, 4]

As we can see in the above example, if the array compact! is being called on does have nils in it, the return value is the modified state of the Array.

But if you call Array#compact! on an array that does not have any nils in it, the return value of Array#compact! is nil.

Array.new(9){false}.compact!
# => nil

–Proctor

Ruby Tuesday – Array#-

Today’s Ruby Tuesday is on Array#-.

Array#-, also called Array Difference, operates against an array, takes another array as its argument, and returns the items in the source array that are not in the second array.

[1, 2, 3, 4, 5] - [1, 2, 3, 5, 8]
# => [4]
[1, 2, 3, 4, 5] - [1, 2, 3, 6, 7, 8]
# => [4, 5]
[1, 2, 3, 6, 7, 8] - [1, 2, 3, 4, 5]
# => [6, 7, 8]
["a", "b", "c", "d"] - ["a", "s", "d", "f"]
# => ["b", "c"]
["a", "b", "c", "d"] - []
# => ["a", "b", "c", "d"]
[] - ["a", "s", "d", "f"]
# => []

As it is doing a difference between the two arrays, let’s add in some duplicate items and see what happens.

[1, 1, 2, 3, 5] - [2, 4, 6, 8]
# => [1, 1, 3, 5]
[1, 2, 2, 3,] - [2, 4, 6, 8]
# => [1, 3]

Based on the results we see that if there was a duplicate item in the source list that wasn’t in the second array, then we leave all those items in the source list.

If we have an item that is duplicated in the first list, and has an entry in the second list as well, then all occurences of that item get removed.

If we wanted more of a set difference behavior, we would have to either call uniq on at least the first array, if not both, or call uniq on the result of the difference.

[1, 1, 2, 3, 5].uniq - [2, 4, 6, 8].uniq
# => [1, 3, 5]
(main)> ([1, 1, 2, 3, 5] - [2, 4, 6, 8]).uniq
# => [1, 3, 5]

–Proctor

Ruby Tuesday – Array#&

Today’s Ruby Tuesday continues on with the Array class and takes a look at Array#*.

Array#* does a set intersection between two arrays1, and an empty array is treated as an empty set.

[1, 2, 3, 4, 5] & []
# => []
[1, 2, 3, 4, 5] & [1, 3, 5]
# => [1, 3, 5]
[1, 2, 3, 4, 5] & [1, 3, 5, 7, 9]
# => [1, 3, 5]
[:a, :b, :c, :d] & [:a, 2, :foo]
# => [:a]
[:d] & [:a, :b, :c, :d]
# => [:d]

The Ruby documentation pages mentions that it uses the eql? and hash methods to determine if two elements are equal, so the elements only have to have the same value and not be the same reference.

[[1, 2], [3, 4], ["a", "b"]] & [[3, 4]]
# => [[3, 4]]
[3, 4].equal? [3, 4]
# => false
[3, 4].eql? [3, 4]
# => true

Array#& also keeps the order of the items as found in the original list for the items in the return value.

["a", "b", "c", "d", "e"] & ["c", "a", "t"]
# => ["a", "c"]

And as mentioned above, it is a set intersection, so no matter how many times an item appears in either array, it is only returned once.

[1, 2, 1, 3] & [1, 2]
# => [1, 2]
[1, 2, 1, 3] & [1, 1, 2]
# => [1, 2]
[1, 2, 1, 3] & [1, 1]
# => [1]

And by turning a String into an Array of characters, we can find all characters that are common between two strings.

"Supercalifragilisticexpialidocious".chars & "Me Ol' Bamboo".chars
# => ["e", "a", "l", "o"]

–Proctor


1. For those who are unfamiliar with what a set intersection is, it is finds all unique elements that are common between the two arrays.

Ruby Tuesday – Array#*

Today’s Ruby Tuesday cover’s Array#*.

Array#* either takes a integer, N, as its argument, in which case it concatenates N copies of the array together and uses the result as it return value.

[1, 2, 3, 4, 5] * 3
% => [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
["a", "b", "c"] * 4
% => ["a", "b", "c", "a", "b", "c", "a", "b", "c", "a", "b", "c"]
[1, [:a, [:b, [:c]]]] * 2
% => [1, [:a, [:b, [:c]]], 1, [:a, [:b, [:c]]]]

Or it can take a string as its argument, in which case, it returns a flattened join of the items in the array with the string argument as the separator.

["a", "b", "c"] * ","
% => "a,b,c"
["a", "b", "c"] * "at"
% => "aatbatc"
[1, 2, 3, 4, 5] * ''
% => "12345"
[:x, :y, :z] * ';'
% => "x;y;z"
[[:a, 1], [:b, 2], [:c, 3]] * ';'
% => "a;1;b;2;c;3"
[1, [:a, [:b, [:c]]]] * ';'
% => "1;a;b;c"

Notice how multiplying by a string returns a flattened array joined together, instead of just the top level elements of an array with to_s called on them joined together.

–Proctor

Ruby Tuesday – Array#first

As we continue looking to see how Ruby’s Array class can be seen as a queue, we take a look at Array#first.

Array#first returns the first element of an Array, or nil if the array is empty.

[1, 2 ,3, 4, 5].first
# => 1
[].first
# => nil
[{:a => 1}, :b, [1, 2, 3]].first
# => {:a=>1}

While this in and of itself is nothing spectacular, what it gets us is a way of having Command/Query Separation1, or CQS for short.

Why care about Command/Query Separation?

First, by using it we can start to take a more functional approach to our design. We can “peek” at the first element of an array, and keep peeking to our hearts content and never worry about modifying the state of the queue.

Second, when it is time to “pop” the next element off the queue, we can have the result of the “pop” command be to return a new queue object that is the result of dropping the first item from the array.

This allows us to empty a queue without modifying the original queue.

my_queue = [1, 2, 3, 4, 5]
# => [1, 2, 3, 4, 5]
my_queue.first #peek
# => 1
my_queue.first #peek again
# => 1
after_pop = my_queue.drop(1)
# => [2, 3, 4, 5]
after_pop
# => [2, 3, 4, 5]
my_queue
# => [1, 2, 3, 4, 5]

–Proctor


1. Command/Query Separation states that a method should either return a value, or modify state, but never do both.

Ruby Tuesday – Array#drop

Today’s Ruby Tuesday continues with looking at the Array class type as a queue, and covers Array#drop.

While Ruby does not have a tail function on Array, you can emulate that functionality by calling Array#drop and passing a 1 as the argument to the method.

Array#drop takes a non-negative integer, “N”, as it’s argument and returns the an array of elements with the first N-elements removed.

[1, 2, 3, 4, 5].drop 1
# => [2, 3, 4, 5]
[1, 2, 3, 4, 5].drop 3
# => [4, 5]
[1, 2, 3, 4, 5].drop 0
# => [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5].drop -1
# ArgumentError: attempt to drop negative size
# from (pry):9:in `drop'

If we call Array#drop on an empty array, we get back an empty array as well.

[].drop 1
# => []

And unlike other methods on Array, like Array#pop from last time, which mutate the underlying value (and don’t end with a !) we can see that calling Array#drop does not modify the original array object.

my_queue = [1, 2, 3, 4, 5]
# => [1, 2, 3, 4, 5]
my_queue.drop 1
# => [2, 3, 4, 5]
my_queue
# => [1, 2, 3, 4, 5]

Trying it out, we also see that there is not a destructive version of the drop method, as we get a NoMethodError when we try it.

my_queue.drop! 1
# NoMethodError: undefined method `drop!' for [1, 2, 3, 4, 5]:Array
# from (pry):13:in `__pry__'

–Proctor

Ruby Tuesday – Array#pop

In today’s Ruby Tuesday we continue from last week’s post on Array#push and cover Array#pop.

Array#pop will remove the last element from an Array, and return that element.

[1, 2, 3, 4, 5].pop
# => 5
[:a, :b, :c, :d, :e].pop
# => :e

By assigning a new array to a variable, and then calling pop on the array, we can see that it does in fact mutate the array, and the last item is no longer there.

stack = [1, 2, 3, 4, 5]
# => [1, 2, 3, 4, 5]
stack.pop
# => 5
stack
# => [1, 2, 3, 4]

Ruby makes the decision to return nil if you call Array#pop on an empty array instead of raising an exception.

[].pop
# => nil

While being safer as far as not causing an exception, it does introduce some danger, as you can keep calling pop on an empty array and it will keep returning nils

10.times{ empty_array.pop }
=> 10

Another potential way there is some danger, is that if you just use Array#pop by itself, you can’t distinguish between an empty array, and an array with nils.

stack_with_nils = [1, nil, 2, nil]
=> [1, nil, 2, nil]
[14] pry(main)> stack_with_nils.pop
=> nil
[15] pry(main)> stack_with_nils
=> [1, nil, 2]

And unlike Array#push, which returns self, Array#pop returns the element popped, which will not allow you to chain commands like Array#push does.

[:a, :b, :c, :d, :e].pop.pop
# NoMethodError: undefined method `pop' for :e:Symbol
# from (pry):4:in `__pry__'

If you wish to pop multiple items from the end of the array, you can pass in a non-negative integer value to Array#pop. When passing a number of items to remove, Array#pop returns a list, with the earlier items popped off the array at the back of the list, and the most recent items at the front of the array.

[:a, :b, :c, :d, :e].pop 3
# => [:c, :d, :e]
third, second, first = [:a, :b, :c, :d, :e].pop 3
# => [:c, :d, :e]
first
# => :e
second
# => :d
third
# => :c

–Proctor

Ruby Tuesday – Array#push

Today’s Ruby Tuesday is on Array#push.

Array#push takes a item and appends that item to an existing Array.

[].push 1
# => [1]
[3, 7, 5].push 1
# => [3, 7, 5, 1]
[3, 7, 5].push []
# => [3, 7, 5, []]
[3, 7, 5].push nil
# => [3, 7, 5, nil]

Array#push also returns itself as the return value, allowing you to chain together calls.

[3, 7, 5].push(2).push(8).push(6)
# => [3, 7, 5, 2, 8, 6]
q = [3, 7, 5]
# => [3, 7, 5]
q.push(2).push(8).push(6)
# => [3, 7, 5, 2, 8, 6]
q
# => [3, 7, 5, 2, 8, 6]

You can also pass more than one argument to Array#push, and it will push each of those items to the Array in turn.

[3, 7, 5].push(2, 8, 6)
# => [3, 7, 5, 2, 8, 6]
[3, 7, 5].push(*[2, 8, 6])
# => [3, 7, 5, 2, 8, 6]

–Proctor

Ruby Tuesday – File::exist?

Today’s Ruby Tuesday is on File::exist?.

There are times when you want to know if a given file or directory exists; usually this is captured from some kind of configuration file, and you want to make sure the item exists before you want to try to process that path so you can give a nice error. Enter File::exist?.

File::exist? will check to see if the file or directory passed as an argument can be seen by Ruby.

File.exist? "foo"
# => false
File.exist? "junk"
# => true
File.exist? "/usr/local/bin"
# => true
File.exist? "/usr/local/junk"
# => false
File.exist? "."
# => true
File.exist? ".."
# => true

–Proctor

Ruby Tuesday – Date#cweek

Today’s Ruby Tuesday is on Date#cweek.

Date#cweek returns a integer value representing the week of the year that the date falls on. The return value is an integer between 1 and 53.

Date.new(2015, 5, 3).cweek
# => 18
Date.new(2015, 5, 4).cweek
# => 19

If we use this week as an example, we can see that a week starts on Monday (the 4th of May), where the Sunday before (May 3rd) was the previous week.

We see that January 1st falls on the first week of the year, no surprise there, and that the 31st of December for 2015, is on the 53rd week of the year.

Date.new(2015, 1, 1).cweek
# => 1
Date.new(2015, 12, 31).cweek
# => 53

Having a 53rd week of the year sounds suprising at first, because everyone talks about 52 weeks in a year, until you realize that sometimes December 31st sometimes falls at the very beginning of a week, causing it to be the 53rd week, since it is only a partial week.

–Proctor