[SOLUTION] Checking Credit Cards (#122)

Let’s try this again. List ate my post. Maybe it’s the S/MIME sig?


OK, this is the first Quiz contribution I have made (publicly). I
think it is a good balance between terse and readable.

JEG2, the luhn_valid? check is what I mean by Functional Programming
in Ruby, or as you say, the Power of Iterators. :slight_smile:

–be

#!/usr/bin/env ruby -wKU

Some utility functions first

require ‘enumerator’
module Enumerable

Maps n-at-a-time (n = arity of given block) and collects the

results
def mapn(&b)
r = []
each_slice(b.arity) {|*args| r << b.call(*args) }
r
end

def sum; inject(0){|s, i| s + i} end
end

class CreditCardNumber < String
TYPES = {“3[47]\d{13}” => “Amex”,
“6011\d{12}” => “Discover”,
“5[1-5]\d{14}” => “Mastercard”,
“4(\d{12}|\d{15})” => “Visa”}

Returns the type of the given card, or nil if the card does not

match a pattern
def card_type
(t = TYPES.detect{|re, t| /^#{re}$/ === self}) && t.last
end

Returns true iff Luhn check passes for this number

def luhn_valid?
# a trick: double_and_sum[8] == sum_digits(8*2) == sum_digits
(16) == 1 + 6 == 7
double_and_sum = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
split(//).reverse.mapn{|a,b| a.to_i + double_and_sum
[b.to_i]}.sum % 10 == 0
end

Returns true iff card matches a known type and Luhn check passes.

def valid?
luhn_valid? && !card_type.nil?
end
end

if (arg = ARGV.join.gsub(/[^0-9]/, ‘’)) and !arg.empty?
number = CreditCardNumber.new(arg)

if number.valid?
puts “Valid #{number.card_type}”
else
puts “Invalid card”
end
end

On Apr 29, 2007, at 1:17 PM, Brad E. wrote:

OK, this is the first Quiz contribution I have made (publicly).

Wow, a semi-local solving the quiz. I guess that means you need to
get back to another OK.rb meeting Brad. :wink:

Welcome to all the new solvers!

James Edward G. II

On Apr 29, 2007, at 1:17 PM, Brad E. wrote:

Maps n-at-a-time (n = arity of given block) and collects the

results
def mapn(&b)
r = []
each_slice(b.arity) {|*args| r << b.call(*args) }
r
end

That’s pretty darn clever. You can collapse it to one line with
inject() of course:

def mapn(&b)
enum_slice(b.arity).inject([]) {|r, args| r << b.call(*args) }
end

James Edward G. II

On May 2, 2007, at 8:57 AM, James Edward G. II wrote:

That’s pretty darn clever. You can collapse it to one line with
inject() of course:

def mapn(&b)
enum_slice(b.arity).inject([]) {|r, args| r << b.call(*args) }
end

Didn’t even think about inject here. Awesome. I’m sure that I’m going
to be using mapn more, as it’s cool.

I can’t take credit for the mapn concept though: eachn is a Facets
function I stumbled across.

–be

James Edward G. II schrieb:

That’s pretty darn clever. You can collapse it to one line with
inject() of course:

def mapn(&b)
enum_slice(b.arity).inject([]) {|r, args| r << b.call(*args) }
end

Simpler:

def mapn(&b)
to_enum(:each_slice, b.arity).map(&b)
end

Regards,
Pit