# \[SOLUTION\] Checking Credit Cards (#122)

**URL:** <https://www.ruby-forum.com/t/solution-checking-credit-cards-122/93235>\
**Category:** Ruby\
**Created:** [April 29, 2007, 8:18pm UTC](https://www.ruby-forum.com/t/solution-checking-credit-cards-122/93235 "2007-04-29T20:18:21Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![bradeck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/7cd45c/32.png) [@bradeck](https://www.ruby-forum.com/u/bradeck)\
**Post date:** [April 29, 2007, 8:18pm UTC](https://www.ruby-forum.com/t/solution-checking-credit-cards-122/93235/1 "2007-04-29T20:18:21Z")

</div>

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. 🙂

–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

---

<div class="post-metadata">

**Author:** ![bradeck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/7cd45c/32.png) [@bradeck](https://www.ruby-forum.com/u/bradeck)\
**Post date:** [May 2, 2007, 2:06pm UTC](https://www.ruby-forum.com/t/solution-checking-credit-cards-122/93235/2 "2007-05-02T14:06:13Z")

</div>

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. 😉

Welcome to all the new solvers!

James Edward G. II

---

<div class="post-metadata">

**Author:** ![bradeck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/7cd45c/32.png) [@bradeck](https://www.ruby-forum.com/u/bradeck)\
**Post date:** [May 2, 2007, 3:57pm UTC](https://www.ruby-forum.com/t/solution-checking-credit-cards-122/93235/3 "2007-05-02T15:57:23Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![bradeck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/7cd45c/32.png) [@bradeck](https://www.ruby-forum.com/u/bradeck)\
**Post date:** [May 2, 2007, 6:45pm UTC](https://www.ruby-forum.com/t/solution-checking-credit-cards-122/93235/4 "2007-05-02T18:45:50Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![bradeck](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/7cd45c/32.png) [@bradeck](https://www.ruby-forum.com/u/bradeck)\
**Post date:** [May 2, 2007, 7:27pm UTC](https://www.ruby-forum.com/t/solution-checking-credit-cards-122/93235/5 "2007-05-02T19:27:56Z")

</div>

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
