Counting capital letters in Ruby [closed] - ruby

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I want to count the number of capital letters to detect the percentage of capital letters in a string. I tried to do it with a regular expression
string.match(/[A-Z]*/), but that will only match the first combination of capital letters.

string.scan() applies to the entire string, and should work for your use-case. The following should work:
your_string = "Hello World"
capital_count = your_string.scan(/[A-Z]/).length

Here are some ways that do not involve converting the string to an array of characters.
CAPS = ('A'..'Z')
ALL_CAPS = CAPS.to_a.join
#=> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CHAR_TO_BIN = 128.times.with_object({}) do |i,h|
c = i.chr
h[c] = (CAPS.cover?(c) ? 1 : 0)
end
#=> {"\x00"=>0, "\x01"=>0, "\x02"=>0,...," "=>0, "!"=>0,...,
"0"=>0, "1"=>0,..."9"=>0, ":"=>0, ";"=>0, "<"=>0, "="=>0,
">"=>0, "?"=>0, "#"=>0, "A"=>1, "B"=>1,..."Z"=>1, "["=>0,...,
"a"=>0, "b"=>0,...,"z"=>0, "{"=>0,...,"\x7F"=>0}
str = "The quick brown dog, 'Lightning', jumped over 'Bubba', the lazy fox"
1: Not very efficient, but Fastest by far and reads well
str.count(ALL_CAPS)
#=> 3
2: Efficient
str.each_char.reduce(0) { |t,c| t + (CAPS.cover?(c) ? 1 : 0) }
#=> 3
3: If you need to do it many times (may be faster than #2)
str.each_char.reduce(0) { |t,c| t + CHAR_TO_BIN[c] }
#=> 3
4: Delete all non-caps and count
str.gsub(/[^A-Z]/,'').size
#=> 3
or delete all caps and count:
str.size - str.gsub(/[A-Z]/,'').size
#=> 3

I thought it would be interesting to compare the efficiency of the various methods suggested.
require 'fruity'
CAPS = ('A'..'Z')
ALL_CAPS = CAPS.to_a.join
#=> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CHAR_TO_BIN = 128.times.with_object({}) do |i,h|
c = i.chr
h[c] = (CAPS.cover?(c) ? 1 : 0)
end
lower = ('a'..'z').to_a
upper = ('A'..'Z').to_a
L = 50_000
U = 10_000
The test string contains L randomly-drawn lower case letters and U randomly-drawn upper-case letters, shuffled.
str = L.times.map {lower.sample}.concat(U.times.map {upper.sample}).shuffle.join
compare do
scan { str.scan(/[A-Z]/).length }
count { str.count(ALL_CAPS) }
reduce { str.each_char.reduce(0) { |t,c| t + (CAPS.cover?(c) ? 1 : 0) } }
hsh { str.each_char.reduce(0) { |t,c| t + CHAR_TO_BIN[c] } }
gsubA { str.gsub(/[^A-Z]/,'').size }
gsubB { str.size - str.gsub(/[A-Z]/,'').size }
end
Running each test 32 times. Test will take about 33 seconds.
count is faster than gsubB by 39x ± 10.0
gsubB is similar to scan
scan is faster than gsubA by 3x ± 1.0
gsubA is similar to hsh
hsh is similar to reduce
I was amazed by how fast String#count is. I had assumed that Ruby would do an include? for every character in the string. I was wrong. Looking at the source code there is a C function tr_setup_table, suggesting that Ruby is constructing a hash or something similar before doing the counting.

Related

Using regular expressions to multiply and sum numeric string characters contained in a hash of mixed numeric strings

Without getting too much into biology, Proteins are made of Amino Acids. Each of the 20 Amino Acids that make up Proteins are represented by characters in a sequence. Each Amino Acid char has a different chemical formula, which I represent as strings. For example, "M" has a formula of "C5H11NO2S"
Given the 20 different formulas (and the varying frequency of each amino acid chars in a protein sequence) I want to compile all 20 of them into a single formula that will yield the total formula for the protein.
So first: multiply each formula by the frequency of its char in the sequence
Second : sum together all multiplied formulas into one formula.
To accomplish this, I first tried multiplying each amino acid char frequency in the sequence by the numbers in the chemical formula. I did this using .tally
sequence ="MGAAARTLRLALGLLLLATLLRPADACSCSPVHPQQAFCNADVVIRAKAVSEKEVDSGNDIYGNPIKRIQYEIKQIKMFKGPEKDIEFI"
sequence.chars.string.tally --> {"M"=>2, "G"=>5, "A"=>11, "R"=>5, "T"=>2, "L"=>9, "P"=>5, "D"=>5, "C"=>3, "S"=>4, "V"=>5, "H"=>1, "Q"=>4, "F"=>3, "N"=>3, "I"=>8, "K"=>7, "E"=>5, "Y"=>2}
Then, I listed all the amino acids chars and formulas into a hash
hash_of_formulas = {"A"=>"C3H7NO2", "R"=>"C6H14N4O2", "N"=>"C4H8N2O3", "D"=>"C4H7NO4", "C"=>"C3H7NO2S", "E"=>"C5H9NO4", "Q"=>"C5H10N2O3", "G"=>"C2H5NO2", "H"=>"C6H9N3O2", "I"=>"C6H13NO2", "L"=>"C6H13NO2", "K"=>"C6H14N2O2", "M"=>"C5H11NO2S", "F"=>"C9H11NO2", "P"=>"C5H9NO2", "S"=>"C3H7NO3", "T"=>"C4H9NO3", "W"=>"C11H12N2O2", "Y"=>"C9H11NO3", "V"=>"C5H11NO2"}
An example of what the process for my overall goal is:
In the sequence , "M" occurs twice so "C5H11NO2S" will become "C10H22N2O4S2". "C" has a formula of "C3H7NO2S" occurs 3 times: In the sequence so "C3H7NO2S" becomes "C9H21N3O6S3"
So, Summing together "C10H22N2O4S2" and "C9H21N3O6S3" will yield "C19H43N5O10S5"
How can I repeat the process of multiplying each formula by its frequency and then summing together all multiplied formulas?
I know that I could use regex for multiplying a formula by its frequency for an individual string using
formula_multiplied_by_frequency = "C5H11NO2S".gsub(/\d+/) { |x| x.to_i * 4}
But I'm not sure of any methods to use regex on strings embedded within hashes
If I understand correctly, you want the to provide the total formula for a given protein sequence. Here's how I'd do it:
NUCLEOTIDES = {"A"=>"C3H7NO2", "R"=>"C6H14N4O2", "N"=>"C4H8N2O3", "D"=>"C4H7NO4", "C"=>"C3H7NO2S", "E"=>"C5H9NO4", "Q"=>"C5H10N2O3", "G"=>"C2H5NO2", "H"=>"C6H9N3O2", "I"=>"C6H13NO2", "L"=>"C6H13NO2", "K"=>"C6H14N2O2", "M"=>"C5H11NO2S", "F"=>"C9H11NO2", "P"=>"C5H9NO2", "S"=>"C3H7NO3", "T"=>"C4H9NO3", "W"=>"C11H12N2O2", "Y"=>"C9H11NO3", "V"=>"C5H11NO2"}
NUCLEOTIDE_COMPOSITIONS = NUCLEOTIDES.each_with_object({}) { |(nucleotide, formula), compositions|
compositions[nucleotide] = formula.scan(/([A-Z][a-z]*)(\d*)/).map { |element, count| [element, count.empty? ? 1 : count.to_i] }.to_h
}
def formula(sequence)
sequence.each_char.with_object(Hash.new(0)) { |nucleotide, final_counts|
NUCLEOTIDE_COMPOSITIONS[nucleotide].each { |element, element_count|
final_counts[element] += element_count
}
}.map { |element, element_count|
"#{element}#{element_count.zero? ? "" : element_count}"
}.join
end
sequence = "MGAAARTLRLALGLLLLATLLRPADACSCSPVHPQQAFCNADVVIRAKAVSEKEVDSGNDIYGNPIKRIQYEIKQIKMFKGPEKDIEFI"
p formula(sequence)
# => "C434H888N51O213S"
You can't use regexp to multiply things. You can use it to parse a formula, but then it's on you and regular Ruby to do the math. The first job is to prepare a composition lookup by breaking down each nucleotide formula. Once we have a composition hash for each nucleotide, we can iterate over a nucleotide sequence, and add up all the elements of each nucleotide.
BTW, tally is not particularly useful here, since tally will need to iterate over the sequence, and then you have to iterate over tally anyway — and there is no aggregate operation going on that can't be done going over each letter independently.
EDIT: I probably made the regexp slightly more complicated that it needs to be, but it should parse stuff like CuSO4 correctly. I don't know if it's an accident or not that all nucleotides are only composed of elements with a single-character symbol... :P )
Givens
We are given a string representing a protein comprised of amino acids:
sequence = "MGAAARTLRLALGLLLLATLLRPADACSCSPVHPQQAFCNADVVIR" +
"AKAVSEKEVDSGNDIYGNPIKRIQYEIKQIKMFKGPEKDIEFI"
and a hash that contains the formulas of amino acids:
formulas = {
"A"=>"C3H7NO2", "R"=>"C6H14N4O2", "N"=>"C4H8N2O3", "D"=>"C4H7NO4",
"C"=>"C3H7NO2S", "E"=>"C5H9NO4", "Q"=>"C5H10N2O3", "G"=>"C2H5NO2",
"H"=>"C6H9N3O2", "I"=>"C6H13NO2", "L"=>"C6H13NO2", "K"=>"C6H14N2O2",
"M"=>"C5H11NO2S", "F"=>"C9H11NO2", "P"=>"C5H9NO2", "S"=>"C3H7NO3",
"T"=>"C4H9NO3", "W"=>"C11H12N2O2", "Y"=>"C9H11NO3", "V"=>"C5H11NO2"
}
Obtain counts of atoms in each amino acid
As a first step we can calculate the numbers of each atom in each amino acid:
counts = formulas.transform_values do |s|
s.scan(/[CHNOS]\d*/).
each_with_object({}) do |s,h|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
end
#=> {"A"=>{"C"=>3, "H"=>7, "N"=>1, "O"=>2},
# "R"=>{"C"=>6, "H"=>14, "N"=>4, "O"=>2},
# ...
# "M"=>{"C"=>5, "H"=>11, "N"=>1, "O"=>2, "S"=>1}
# ...
# "V"=>{"C"=>5, "H"=>11, "N"=>1, "O"=>2}}
Compute formula for protein
Then it's simply:
def protein_formula(sequence, counts)
sequence.each_char.
with_object("C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0) do |c,h|
counts[c].each { |aa,cnt| h[aa] += cnt }
end.each_with_object('') { |(aa,nbr),s| s << "#{aa}#{nbr}" }
end
protein_formula(sequence, counts)
#=> "C434H888N120O213S5"
Another example:
protein_formula("MCMPCFTTDHQMARKCDDCCGGKGRGKCYGPQCLCR", count)
#=> "C158H326N52O83S11"
Explanation of calculation of counts
This calculation:
counts = formulas.transform_values do |s|
s.scan(/[CHNOS]\d*/).each_with_object({}) do |s,h|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
end
uses the method Hash#transform_values. It will return a hash having the same keys as the hash formulas, with the values of those keys in formula modified by transform_values's block. For example, formulas["A"] ("C3H7NO2") is "transformed" to the hash {"C"=>3, "H"=>7, "N"=>1, "O"=>2} in the hash that is returned, counts.
transform_values passes each value of formulas to the block and sets the block variable equal to it. The first value passed is "C3H7NO2", so it sets:
s = "C3H7NO2"
We can write the block calculation more simply:
h = {}
s.scan(/[CHNOS]\d*/).each do |s|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
h
(Once you understand this calculation, which I explain below, see Enumerable#each_with_object to understand why I used that method in my solution.)
After initializing h to an empty hash, the following calculations are performed:
h = {}
a = s.scan(/[CHNOS]\d*/)
#=> ["C3", "H7", "N", "O2"]
a is computed using String#scan with the regular expression /[CHNOS]\d*/. That regular expression, or regex, matches exactly one character in the character class [CHNOS] followed by zero of more (*) digits (\d). It therefore separates the string "C3H7NO2" into the substrings that are returned in the array shown under the calculation of a above . Continuing,
a.each do |s|
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
end
changes h to the following:
h #=> {"C"=>3, "H"=>7, "N"=>1, "O"=>2}
The block variable s is initially set equal to the first element of a that is passed to each's block:
s = "C3"
then we compute:
h[s[0]] = s.size == 1 ? 1 : s[1..-1].to_i
h["A"] = 2 == 1 ? 1 : "3".to_i
= false ? 1 : 3
3
This is repeated for each element of a.
Exclamation of construction of formula for the protein
We can simplify the following code1:
sequence.each_char.with_object("C"=>0, "H"=>0, "N"=>0, "O"=>0) do |c,h|
counts[c].each { |aa,cnt| h[aa] += cnt }
end.each_with_object('') { |(aa,nbr),s| s << "#{aa}#{nbr}" }
to more or less the following:
h = { "C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0 }
ch = sequence.chars
#=> ["M", "G", "A",..., "F", "I"]
ch.each do |c|
counts[c].each { |aa,cnt| h[aa] += cnt }
end
h #=> {"C"=>434, "H"=>888, "N"=>120, "O"=>213, "S"=>5}
When the first value of ch ("M") is passed to each's block (when h = { "C"=>0, "H"=>0, "N"=>0, "O"=>0, "S"=>0 }), the following calculations are performed:
c = "M"
g = counts[c]
#=> {"C"=>10, "H"=>22, "N"=>2, "O"=>4, "S"=>1}
g.each { |aa,cnt| h[aa] += cnt }
h #=> {"C"=>10, "H"=>22, "N"=>2, "O"=>4, "S"=>1}
Lastly, (when h #=> {"C"=>434, "H"=>888, "N"=>120, "O"=>213, "S"=>5})
s = ''
h.each { |aa,nbr| s << "#{aa}#{nbr}" }
s #=> "C434H888N120O213S5"
When aa = "C" and nbr = 434,
"#{aa}#{nbr}"
#=> "C434"
is appended to the string s.
1. (("C"=>0, "H"=>0, "N"=>0, "O"=>0) is shorthand for ({"C"=>0, "H"=>0, "N"=>0, "O"=>0}).

Finding Longest Substring No Duplicates - Help Optimizing Code [Ruby]

So I've been trying to solve a Leetcode Question, "Given a string, find the length of the longest substring without repeating characters."
For example
Input: "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.
Currently I optimized my algorithm when it comes to figuring out if the substring is unique by using a hash table. However my code still runs in O(n^2) runtime, and as a result exceeds the time limit during submissions.
What i try to do is to essentially go through every single possible substring and check if it has any duplicate values. Am I as efficient as it gets when it comes to the brute force method here? I know there's other methods such as a sliding window method but I'm trying to get the brute force method down first.
# #param {String} s
# #return {Integer}
def length_of_longest_substring(s)
max_length = 0
max_string = ""
n = s.length
for i in (0..n-1)
for j in (i..n-1)
substring = s[i..j]
#puts substring
if unique(substring)
if substring.length > max_length
max_length = substring.length
max_string = substring
end
end
end
end
return max_length
end
def unique(string)
hash = Hash.new(false)
array = string.split('')
array.each do |char|
if hash[char] == true
return false
else
hash[char] = true
end
end
return true
end
Approach
Here is a way of doing that with a hash that maps characters to indices. For a string s, suppose the characters in the substring s[j..j+n-1] are unique, and therefore the substring is a candidate for the longest unique substring. The next element is therefore e = s[j+n] We wish to determine if s[j..j+n-1] includes e. If it does not we can append e to the substring, keeping it unique.
If s[j..j+n-1] includes e, we determine if n (the size of the substring) is greater than the length of the previously-known substring, and update our records if it is. To determine if s[j..j+n-1] includes e, we could perform a linear search of the substring, but it is faster to maintain a hash c_to_i whose key-value pairs are s[i]=>i, i = j..j_n-1. That is, c_to_i maps the characters in the substring to their indices in full string s. That way we can merely evaluate c_to_i.key?(e) to see if the substring contains e. If the substring includes e, we use c_to_i to determine its index in s and add one: j = c_to_i[e] + 1. The new substring is therefore s[j..j+n-1] with the new value of j. Note that several characters of s may be skipped in this step.
Regardless of whether the substring contained e, we must now append e to the (possibly-updated) substring, so that it becomes s[j..j+n].
Code
def longest_no_repeats(str)
c_to_i = {}
longest = { length: 0, end: nil }
str.each_char.with_index do |c,i|
j = c_to_i[c]
if j
longest = { length: c_to_i.size, end: i-1 } if
c_to_i.size > longest[:length]
c_to_i.reject! { |_,k| k <= j }
end
c_to_i[c] = i
end
c_to_i.size > longest[:length] ? { length: c_to_i.size, end: str.size-1 } :
longest
end
Example
a = ('a'..'z').to_a
#=> ["a", "b",..., "z"]
str = 60.times.map { a.sample }.join
#=> "ekgdaxxzlwbxixhlfbpziswcoelplhobivoygmupdaexssbuuawxmhprkfms"
longest = longest_no_repeats(str)
#=> {:length=>14, :end=>44}
str[0..longest[:end]]
#=> "ekgdaxxzlwbxixhlfbpziswcoelplhobivoygmupdaexs"
str[longest[:end]-longest[:length]+1,longest[:length]]
#=> "bivoygmupdaexs"
Efficiency
Here is a benchmark comparison to #mechnicov's code:
require 'benchmark/ips'
a = ('a'..'z').to_a
arr = 50.times.map { 1000.times.map { a.sample }.join }
Benchmark.ips do |x|
x.report("mechnicov") { arr.sum { |s| max_non_repeated(s)[:length] } }
x.report("cary") { arr.sum { |s| longest_no_repeats(s)[:length] } }
x.compare!
end
displays:
Comparison:
cary: 35.8 i/s
mechnicov: 0.0 i/s - 1198.21x slower
From your link:
Input: "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
That means you need first non-repeated substring.
I suggest here is such method
def max_non_repeated(string)
max_string = string.
each_char.
map.with_index { |_, i| string[i..].split('') }.
map do |v|
ary = []
v.each { |l| ary << l if ary.size == ary.uniq.size }
ary.uniq.join
end.
max
{
string: max_string,
length: max_string.length
}
end
max_non_repeated('pwwkew')[:string] #=> "wke"
max_non_repeated('pwwkew')[:length] #=> 3
In Ruby < 2.6 use [i..-1] instead of [i..]

Float - two digits after comma - how to?

I tried to execute a calculation in Ruby. The result I get is 1589.5833333333333. I would like to limitat the numbers of digits after the comma.
The result should always be limited to 2 digits as followed:
1589.58
Question #1 = how can I set the limitation?
Question #2 = how can I round up 1589.60 or down 1589.55
Many thanks for help. Language is ruby
Other option but keeping the object as Float:
n = 1589.5833333333333
m = n.truncate(2) #=> 1589.58
h = n.round(1) #=> 1589.6 # for the last zero you need to format the string
And a tricky:
k = (n*100).to_i.digits.tap{ |ary| ary.first > 5 ? ary[0] = 5 : ary[0] = 0 }.reverse.join('').to_i/100.0
#=> 1589.55
For question 1:
num = 1589.5833333333333
printf('%.2f', num)
=> 1589.58
For question 2 to round up to first digit:
num = 1589.5833333333333
printf('%.2f', num.round(1))
=> 1589.60
1589.55 is a bit of an arbitrary number, rounding down would usually be calculated as 1589.58. I don't know of any Ruby function that does that off-hand.

taking a random amount of hash pairs and putting it in another ruby [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I'm trying to take several key value pairs at random from one hash to another. Taking n values from h and putting it into i
h = {"a"=>1,"b"=>2,"c"=>3}
n = 2
i = {}
If you are using Ruby 2.5+, take advantage of the sweet new Hash#slice method, which is designed to do exactly this:
>> h = {"a"=>1,"b"=>2,"c"=>3}
>> n = 2
>> h.slice(*h.keys.sample(n))
#> {"c"=>3, "a"=>1}
You can get an array of random keys with keys.sample then iterate over them to add keys and values from h into i.
h = {"a"=>1,"b"=>2,"c"=>3}
n = 2
i = {}
h.keys.sample(n).each { |k| i[k] = h[k] }
i #=> {"b"=>2, "c"=>3}
Or you can just map the sample keys with values from h then convert to a hash.
h.keys.sample(n).map { |k| [k, h[k]] }.to_h
#=> {"b"=>2, "a"=>1}
h = {"a"=>1,"b"=>2,"c"=>3}
n = 2
random_keys = h.keys.sample(n)
i = random_keys.each_with_object({}){|m,o| o[m] = h[m]}
p i
Results:
{"b"=>2, "c"=>3}
sample method will take n random keys as an array.
each_with_objece Iterates elements of random_keys with new hash o.
h = {"a"=>1,"b"=>2,"c"=>3}
n = 2
g = {pet: 'dog'}
rnd_keys = h.keys.sample(n)
#=> ["c", "a"|
g.merge(h.select { |k,_| rnd_keys.include?(k) })
#=> {:pet=>"dog", "a"=>1, "c"=>3}
If, as in the example, g is empty, simply:
h.select { |k,_| rnd_keys.include?(k) }
# => {"a"=>1, "c"=>3}

Refactor Ruby code for Luhn algorithm

Help me refactor implementing Luhn algorithm, which is described as follows:
The formula verifies a number against its included check digit, which
is usually appended to a partial account number to generate the full
account number. This account number must pass the following test:
From the rightmost digit, which is the check digit, moving left, double the value of every second digit; if the product of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then sum the digits of the products (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9).
Take the sum of all the digits.
If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
Assume an example of an account number "7992739871" that will have a
check digit added, making it of the form 7992739871x:
Account number 7 9 9 2 7 3 9 8 7 1 x
Double every other 7 18 9 4 7 6 9 16 7 2 -
Sum of digits 7 9 9 4 7 6 9 7 7 2 =67
The check digit (x) is obtained by computing the sum of digits then
computing 9 times that value modulo 10 (in equation form, (67 × 9 mod
10)). In algorithm form:
Compute the sum of the digits (67).
Multiply by 9 (603).
The last digit, 3, is the check digit. Thus, x=3.
Following is my implementation, it works but could be a lot better, I believe.
def credit_check(num)
verify = num.to_s.split('').map(&:to_i)
half1 = verify.reverse.select.each_with_index { |str, i| i.even? }
half1 = half1.inject(0) { |r,i| r + i }
# This implements rule 1
half2 = verify.reverse.select.each_with_index { |str, i| i.odd? }
double = half2.map { |n| n * 2 }
double = double.map { |n| n.to_s.split('') }
double = double.flatten.map(&:to_i)
double = double.inject(0) { |r,i| r + i }
final = double + half1
puts final % 10 == 0 && (num.to_s.length > 12 && num.to_s.length < 17) ? "VALID" : "INVALID"
end
I'm a rank noob at all of this, obviously. But I appreciate any help, including proper style!
Suggestions:
Try to encapsulate your code in a class and provide a intuitive public API. Hide the inner details of the algorithm in private methods.
Break the rules into small methods in the class that has utmost 5 lines, break this rule sparingly. Follow Sandi Metz Rules.
Study the problem and find domain names relevant to the problem; use it to name the small methods.
Focus on readability. Remember this quote: "Programs must be written for people to read, and only incidentally for machines to execute." by Hal Abelson from SICP.
Read Ruby style guide to improve code formatting; and yes get a better editor.
Following these may seem like making the code more verbose. But it will improve readability and help for maintenance. Also, if you tend to follow it even in personal projects, this process will be etched into you and will soon become second nature.
With these in mind, go through the following attempt at the problem:
class CreditCard
VALID_LENGTH_RANGE = 12..17
def initialize(number)
#number = number.to_s
end
def valid?
valid_length? && check_sum_match?
end
private
def valid_length?
VALID_LENGTH_RANGE.include? #number.length
end
def check_sum_match?
check_sum.end_with? check_digit
end
def check_sum
digits = check_less_number
.reverse
.each_char
.each_with_index
.map do |character, index|
digit = character.to_i
index.even? ? double_and_sum(digit) : digit
end
digits.reduce(:+).to_s
end
def check_less_number
#number[0..-2]
end
def check_digit
#number[-1]
end
def double_and_sum(digit)
double = digit * 2
tens = double / 10
units = double % 10
tens + units
end
end
Hence you can use it as follows:
CreditCard.new(222222222224).valid? # => true
CreditCard.new(222222222222).valid? # => false
how about using nested inject method
half2 = verify.reverse.select.each_with_index { |str, i| i.odd? }
double = half2.map { |n| n * 2 }
double = double.inject(0){|x,y| x + y.to_s.split("").inject(0){|sum, n| sum + n.to_i}}
I would implement that algorithm like that:
def credit_card_valid?(num)
digits = String(num).reverse.chars.map(&:to_i)
digits.each_with_index.reduce(0) do |acc, (value, index)|
acc + if index.even?
value
else
double_value = value * 2
if double_value > 9
double_value.to_s.split('').map(&:to_i).reduce(&:+)
else
double_value
end
end
end % 10 == 0
end
Well, that code works for those examples from Wikipedia :)
Here are some advices for you:
get rid of prints/puts to stdin in your function, just return a
value. For this function boolean true/false is good.
ruby community
uses '?' in method names that return false/true
don't forget about
properly formatting your code, but maybe you might've not yet learnt how to do it on Stackoverflow (I haven't yet :)
use 2 spaces for indenting your code

Resources