I've known about the zip method for arrays but have never really found much of a use for it. I was working a problem on
Programming Praxis the other day and saw some Python solutions that used it, so I decided to give it a try in my solution. Let's start out with how it works.
In the simplest case, we have an array and we zip it with an array of the same size. Here's what it looks like ...
a = [1, 2, 3]
b = [4, 5, 6]
a.zip(b) => [[1,4], [2,5], [3,6]]
We can see that we end up with an array that's the same size as the original arrays made up of elements of each of the arrays. We can also zip multiple arrays ...
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
d = %w[a, b, c]
a.zip(b, c, d) => [[1, 4, 7, "a,"], [2, 5, 8, "b,"], [3, 6, 9, "c"]]
With that here's the documented code for the upside_up program including, at the beginning, the original requirements from Programming Praxis ...
# An “upside up” number is a number that reads the same when it is rotated
# 180°. For instance, 689 and 1961 are upside up numbers.
# Your task is to find the next upside up number greater than 1961, and to
# count the number of upside up numbers less than ten thousand. When you are
# finished, you are welcome to read or run a suggested solution, or to post
# your own solution or discuss the exercise in the comments below.
#
# Create an array of pairs that can match. We should end up with
# UPSIDE_DICT = [[0, 0], [1, 1], [6, 9], [8, 8], [9, 6]]
UPSIDE_DICT = %w[0 1 6 8 9].zip(%w[0 1 9 8 6])
# Open the Integer class and add the upside_up? method that returns true/false
# based on whether the integer is an upside number or not.
# Let's take this a piece at a time:
# 1) self.to_s.split(//) will give us an array of characters for the given number
# such as [1, 9, 6, 1]
# 2) zip this array with
# 3) self.to_s.split(//).reverse will give us the array above reversed ...
# [1, 6, 9, 1]
# 4) and zipping the two together should give us something like ...
# [[1, 1], [9, 6], [6, 9], [1, 1]]
# 5) Now, we'll loop through the above zipped array using inject and make sure that every pair
# in it is also in the UPSIDE_DICT array. If all of them are, then we'll return
# true otherwise the inject() will return false.
class Integer
def upside_up?
self.to_s.split(//).zip(self.to_s.split(//).reverse).inject(true) { |r,v| r && UPSIDE_DICT.include?(v) }
end
end
# Find all the upside values up to 10000 and print them.
(1..10000).each do |v|
puts "#{v} is an upside number" if v.upside_up?
end
Be sure to let me know if you have questions or comments.