Invoice numbers are numeric only with any number of digits. To format one correctly, group the digits in group of three plus a group of any remainder, but never leave one digit by itself, unless it's a one digit number. Eg these are all correct formatting
123
12-34
6
783-907-23-45
And these are not
123-4
98-456
There's one more catch user input is passed directly to the function and you never know what characters users might type. Ignore any part of the input that is not digit
Invoice.format_number should always return a string
module Invoice
def self.format_number(str)
return ""
end
end
puts Invoice.format_number("ab1234")
What I have tried
1st approach
arr = []
str.chars.each do |elem|
val = elem =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
arr << elem if val == 0
end
num_of_digits = arr.length
pairs_of_two = 0
pairs_of_three = 0
if num_of_digits > 5
while num_of_digits > 0 do
break if num_of_digits <= 3
if num_of_digits >= 3 && (num_of_digits % 3 == 0 || num_of_digits % 3 == 2)
pairs_of_three += 1
num_of_digits -= 3
elsif num_of_digits % 2 == 0 || num_of_digits % 2 == 1
pairs_of_two += 1
num_of_digits -= 2
end
end
end
2nd approach
arr = []
str.chars.each do |elem|
val = elem =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
arr << elem if val == 0
end
len = arr.length - 1
if arr.length > 4
str = ""
i = 0
while i < len do
if arr[i..i+3].length == 4
str << arr[i..i+2].join + "-"
i += 3
elsif arr[i..i+2].length == 3
str << arr[i..i+1].join + "-"
i += 2
elsif arr[i..i+1].length == 2
str << arr[i..i+1].join
i += 2
elsif !arr[i].nil?
str << arr[i]
i += 1
end
end
puts str
else
if arr.length <= 3
puts arr.join
else
puts arr[0..1].join + "-" + arr[2..3].join
end
end
But none of them is correct
Here is the function invoice_number in python
def invoice_number(invoice):
s = ''.join(x for x in invoice if x <= '9' and x >= '0')
n = len(s)
if n <= 3:
return s
w = ''
i = 0
while i + 3 <= n:
for j in range(0, 3):
w += s[i + j]
i += 3
w += ('-')
m = n - i
if m == 0: return w[:-1]
if m == 1: return w[:m-3] + '-' + s[-2:]
return w + s[i:]
Testing
print(invoice_number('1234567'))
print(invoice_number('12345678'))
print(invoice_number('abc123456789'))
print(invoice_number('1234abc5678xyz9foobar'))
123-45-67
123-456-78
123-456-789
123-456-789
Eliminating non-digits is easy with re. For your format, the key is to figure our the "right" splitting indices.
Here is a try:
import re
def splits(n, k):
idx = [(i, min(n, i+k)) for i in range(0, n, k)]
if len(idx) > 1:
(a, b), (c, d) = idx[-2:]
if d - c < 2:
idx[-2:] = [(a, b - 1), (c - 1, d)]
return idx
def myformat(s):
s = re.sub(r'[^0-9]+', '', s)
parts = [s[a:b] for a, b in splits(len(s), 3)]
return '-'.join(parts)
Tests:
>>> myformat('123')
123
>>> myformat('1234')
12-34
>>> myformat('6')
6
>>> myformat('7839072345')
783-907-23-45
As the question was asked for ruby, adding solution for ruby. (The inspiration of the code is mostly from #yuri answer)
def format_invoice(invoice)
# only numbers are allowed
invoice = invoice.tr("^0-9","")
#puts invoice
return invoice if(invoice.length <= 3)
formatted_invoice = ''
i = 0
# Loop to divide the invoice in group of 3
while i + 3 <= invoice.length do
for j in 0..2 do
formatted_invoice += invoice[i + j]
end
i += 3
formatted_invoice += ('-')
end
m = invoice.length - i
return formatted_invoice[0..-2] if m == 0
return formatted_invoice[0..m-4] + '-' + invoice[-2..-1] if m == 1
return formatted_invoice + invoice[i..-1]
end
Testing
puts format_invoice('abc1') # 1
puts format_invoice('abc123') # 123
puts format_invoice('abc123A4') # 12-34
puts format_invoice('1234567') # 123-45-67
puts format_invoice('12345678') # 123-456-78
puts format_invoice('abc123456789') # 123-456-789
puts format_invoice('1234a#c5678xyz9foobar') # 123-456-789
FizzBuzz, a classic problem, returns all the numbers up to N with a slight twist. If a number is divisible by 3, it is replaced with "fizz". If it's divisible by 5, it's replaced with "buzz". If it's divisible by both, it's replaced with "fizzbuzz"
I keep getting this Error message:
comparison of Fixnum with nil failed
Can someone explain this error message to me please? Also why is the code not working?
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while arr[i] < arr[n]
if i % 3 == 0 && i % 5 == 0
arr[i] = 'fizzbuzz'
elsif i % 3 == 0
arr[i] = 'fizz'
elsif i % 5 == 0
arr[i] = 'buzz'
else
arr[i] = i
i += 1
end
end
return arr
end
fizz_buzz(12)
Your conditions are just a bit off, give this a try:
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while i < n
if arr[i] % 3 == 0 && arr[i] % 5 == 0
arr[i] = 'fizzbuzz'
elsif arr[i] % 3 == 0
arr[i] = 'fizz'
elsif arr[i] % 5 == 0
arr[i] = 'buzz'
end
i+=1
end
return arr
end
Trying to access arr[n] puts you outside the bounds of the array which returns nil in Ruby.
You can update the code the ruby way, by using blocks and guards, I don't remember last time I used a while loop in ruby :)
Also, Array.new accepts a block as an argument which you can exploit to build your Array in a single step:
def fizz_buzz(n)
Array.new(n) do |index|
x = index + 1
case
when x % 3 == 0 && x % 5 == 0 then "fizzbuzz"
when x % 3 == 0 then "fizz"
when x % 5 == 0 then "buzz"
else x
end
end
end
Notice I used 1 as a base index and not 0, you can just remove x = index + 1 and replace x with index to have it working in a zero index base
A solution with a block instead of the while loop, and guards
def fizz_buzz(n)
arr = (1..n).to_a
0.upto(n - 1) do |i|
arr[i] = "fizzbuzz" and next if i % 3 == 0 && i % 5 == 0
arr[i] = "fizz" and next if i % 3 == 0
arr[i] = "buzz" if i % 5 == 0
end
arr
end
#brad-melanson beat me to the straight-forward answer to your question, so I'll share an answer which uses some common Ruby idioms (passing a range to the Array constructor and map), which simplify things, prevent you from having to do any iteration bookkeeping and prevent the possibility of off-by-one errors, out-of-bounds errors, etc.
def fizz_buzz(n)
Array(1..12).map do |n|
if n % 3 == 0 && n % 5 == 0
'fizzbuzz'
elsif n % 3 == 0
'fizz'
elsif n % 5 == 0
'buzz'
else
n
end
end
end
result = fizz_buzz 12
# result => [1, 2, "fizz", 4, "buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz"]
I'm trying to write some code that will take an array of numbers and print a string representation of the range of the numbers.
def rng (arr)
str = arr[0].to_s
idx = 1
arr.each do |i|
next if arr.index(i) == 0
if arr[arr.index(i)-1] == i - 1
unless str[idx - 1] == "-"
str[idx] = "-"
#else next
end
#puts "if statement str: #{str}, idx: #{idx}"
else
str[idx] = arr[arr.index(i)-1].to_s
idx += 1
str[idx] = ","+ i.to_s
end
idx += 1
end
puts "str = #{str} and idx = #{idx}"
end
rng [0, 1, 2, 3, 8] #"0-3, 8"
I get this error:
arrayRange_0.rb:9:in `[]=': index 3 out of string (IndexError)
Can anyone explain why? When I uncomment the else next it works. Not sure why.
When you get that error, str contains the value 0- which is only 2 characters long - therefore it can't be indexed to the position of 3.
Add this line before line 9, which is causing your error:
puts "str = #{str}, idx = #{idx}"
It will output:
str = 0, idx = 1
str = 0-, idx = 3
Here is how you could do it:
def rng(arr)
ranges = []
arr.each do |v|
if ranges.last && ranges.last.include?(v-1)
# If this is the next consecutive number
# store it in the second element
ranges.last[1] = v
else
# Add a new array with current value as the first number
ranges << [v]
end
end
# Make a list of strings from the ranges
# [[0,3], [8]] becomes ["0-3", "8"]
range_strings = ranges.map{|range| range.join('-') }
range_strings.join(', ')
end
p rng [0, 1, 2, 3, 8]
# results in "0-3, 8"
Like the previous answer says, your index is outside of the string
The error is on line 12 and I'm not sure why I can't add the numbers. Any help is much appreciated.
Instructions: Write a method that takes an array of numbers. If a pair of numbers in the array sums to zero, return the positions of those two numbers. If no pair of numbers sums to zero, return nil.
def two_sum(nums)
idx1 = 0
idx2 = 1
while idx1 < nums.length
if nums[idx1] + nums[idx2] == 0
return [idx1, idx2]
end
idx2 += 1
if idx2 == nums.length
idx1 += 1
idx2 = idx1 + 1
end
end
return nil
end
puts("two_sum([1, 3, 5, -3]) == [1, 3]: #{two_sum([1, 3, 5, -3]) == [1, 3]}")
puts("two_sum([1, 3, 5]) == nil: #{two_sum([1, 3, 5]) == nil}")
idx2 could overflow capacity of your array:
Imagine. nums = [1,2,3], so nums.length is 3, idx1 = 1, idx2 = 2
idx2 += 1 # ok now idx2 is 3
if idx2 == nums.length # ok true, idx2 == 3
idx1 += 1 # mmm, cool idx1 now 2
idx2 = idx1 + 1 # idx2 is 3
end
So in next iteration you will call
nums[idx2]
# same as
nums[3]
# ERROR! there is only 3 numbers in nums
And try to understand this code
def two_sums(nums)
nums[0..-2].each.with_index do |n,i|
nums[i+1..-1].each.with_index do |m,j|
return [i, i+j+1] if m + n == 0
end
end
nil
end
I'm working through the Ruby Koans, and I'm having a bit of trouble figuring out what is going wrong with a method I've written. I'm in about_scoring_project.rb, and I've written the score method for the dice game:
def score(dice)
return 0 if dice == []
sum = 0
rolls = dice.inject(Hash.new(0)) { |result, element| result[element] += 1; result; }
rolls.each { |key, value|
# special condition for rolls of 1
if key == 1
sum += 1000 | value -= 3 if value >= 3
sum += 100*value
next
end
sum += 100*key | value -= 3 if value >= 3
sum += 50*value if key == 5 && value > 0
}
return sum
end
For those unfamiliar with the exercise:
Greed is a dice game where you roll up to five dice to accumulate
points. The following "score" function will be used to calculate the
score of a single roll of the dice.
A greed roll is scored as follows:
A set of three ones is 1000 points
A set of three numbers (other than ones) is worth 100 times the number. (e.g. three fives is 500 points).
A one (that is not part of a set of three) is worth 100 points.
A five (that is not part of a set of three) is worth 50 points.
Everything else is worth 0 points.
Examples:
score([1,1,1,5,1]) => 1150 points score([2,3,4,6,2]) => 0 points
score([3,4,5,3,3]) => 350 points score([1,5,1,2,4]) => 250 points
More scoring examples are given in the tests below:
Your goal is to write the score method.
I run into trouble when I try to run the last test in the file: assert_equal 550, score([5,5,5,5])
For some reason I am returning 551 instead of 550. Thanks for your help!
Here is my approach:
def score(dice)
# Count how many what
clusters = dice.reduce(Hash.new(0)) {|hash, num| hash[num] += 1; hash }
# Since 1's are special, handle them first
ones = clusters.delete(1) || 0
score = ones % 3 * 100 + ones / 3 * 1000
# Then singular 5's
score += clusters[5] % 3 * 50
# Then the triples other than triple-one
clusters.reduce(score) {|s, (num, count)| s + count / 3 * num * 100 }
end
My approach uses two lookup tables - one containing the scores for triples, the other for singles. I work out the score for each number using the tables, and accumulate the total using inject:
def score(dice)
triple_scores = [1000, 200, 300, 400, 500, 600]
single_scores = [100, 0, 0, 0, 50, 0]
(1..6).inject(0) do |score, number|
count = dice.count(number)
score += triple_scores[number - 1] * (count / 3)
score += single_scores[number - 1] * (count % 3)
end
end
I went with
def score(dice)
dice.uniq.map do |die|
count = dice.count die
if count > 2
count -= 3
die == 1 ? 1000 : 100 * die
else 0
end + case die
when 1 then count * 100
when 5 then count * 50
else 0
end
end.inject(:+) || 0
end
This is because you're really adding the result of a | operator (Bitwise OR) to the total score:
sum += 100*key | value -= 3 if value >= 3 # This is 501 in your case
Proof:
irb(main):004:0> value = 4
=> 4
irb(main):005:0> 100 * 5 | value -= 3 # This should be read as (500) | 1 which is 501
=> 501
So rewrite it like this:
if value >= 3
sum += 100 * key
value -= 3
end
My approach was:
def score(dice)
calculator = ->(no, group_multipler, individual_multipler) { (no / 3 * group_multipler) + (no % 3 * individual_multipler) }
dice.group_by {|i| i % 7 }.inject(0) do |total, (value, scores)|
group_multipler, individual_multipler = case value
when 1
[1000, 100]
when 5
[500, 50]
else
[value * 100, 0]
end
total += calculator.call(scores.size, group_multipler, individual_multipler)
end
end
My approach:
def score(dice)
score = 0
score += dice.count(1) >= 3? (1000+ (dice.count(1) -3)*100): dice.count(1) * 100
score += dice.count(5) >= 3 ? (500 + (dice.count(5) -3)*50): dice.count(5) * 50
[2,3,4,6].each {|x| dice.count(x) >=3? score+= x*100:0}
return score
end
Here's my answer:
def score(dice)
frequency = dice.inject(Hash.new(0)) do |h, el|
h[el] += 1
h
end
score_triples = { 1 => 1000 }
score_singles = { 1 => 100, 5 => 50 }
score = 0
frequency.each do |k, v|
score += v / 3 * score_triples.fetch(k, 100 * k)
score += v % 3 * score_singles.fetch(k, 0)
end
score
end
My approach used integer division and modulus division:
def score(dice)
points = 1000 * (dice.count(1) / 3)
points += 100 * (dice.count(1) % 3)
points += 50 * (dice.count(5) % 3)
(2..6).each do |i|
points += (100 * i) * (dice.count(i) / 3)
end
points
end
This was the first piece of code I ever wrote by myself (With a ton of help of stackoverflow, of course.) After watching all other answers I realize it is way overkill specially because it works for a 9 numbers dice (does that exist?)
def score(dice)
if dice.empty?
return 0
end
var_score = 0
conteo = (0..9).to_a.each.map { |x| dice.count(x)}
#Evaluating 1
if ( conteo[1] / 3 ) >= 0
multiplier1 = conteo[1]/3
var_score += multiplier1 * 1000
end
if ( conteo[1] % 3 ) != 0
var_score += (conteo[1] % 3)*100
end
#Evaluating 5
if ( conteo[5] % 3 ) != 0
var_score += (conteo[5] % 3)* 50
end
#Evaluating numbers x 3
if (conteo[2..9].count { |x| x >= 3 }) > 0
triplets = conteo[2..9].map {|x| x / 3}
array_multiplicator = triplets.each_with_index.select {|num,index| (num > 0)}.map {|x| x[0]}
product_triplets = triplets.each_with_index.select {|num,index| (num > 0)}.map {|x| x[1]}.map {|x| (x+2)*100}
var_score += array_multiplicator.zip(product_triplets).map{|x| x.inject(&:*)}.sum
end
var_score
end
It took 29 lines, but this is my first Ruby
def score(dice)
return 0 if dice == []
sums = Array.new # To hold number of occurrences 1 - 6
for i in 0..6 # Initialize to 0... note [0] is not used
sums[i] = 0
end
total = 0 # To hold total
dice.each do |dots| # Number of dots showing on dice
sums[dots] += 1 # Increment the array members 1 - 6
end
if sums[1] > 2 then # If 3 1's
total += 1000
sums[1] -= 3 # Remove the 3 you took, in case there's more
end
if sums[2] > 2 then total += 200 # If 3 2's
end
if sums[3] > 2 then total += 300 #If 3 3's
end
if sums[4] > 2 then total += 400 #If 3 4's
end
if sums[5] > 2 then total += 500 #If 3 5's
sums[5] -= 3 #Remove the 5's you took
end
if sums[6] > 2 then total += 600 #If 3 6's
end
total += (sums[1] * 100) # If any ones are left
total += (sums[5] * 50) # Same for fives
return total
end
This is my solutions.
def score(dice)
score = 0
# grab all the numbers and their amounts
number_amounts = dice.reduce(Hash.new(0)) { |hash, numb| hash[numb] += 1; hash }
# iterate through each pair
number_amounts.each do |key, value|
# case with number 1
score += (value % 3) * 100 + value / 3 * 1000 if (key == 1)
# case with number 5
score += (value % 3) * 50 + value / 3 * key * 100 if (key == 5)
# all numbers except 1 and 5
score += (value / 3) * key * 100 if (key != 1 && key != 5)
end
score
end
def score(dice)
# Set up rules Hash
rules = { 1 => {:triples => 1000, :singles => 100}, 5 => {:triples => 100, :singles => 50} }
[2,3,4,6].each {|i| rules[i] = {:triples => 100, :singles => 0} }
# Count all ocourencies
counts = dice.each_with_object(Hash.new(0)) {|e, h| h[e] += 1}
#calculate total
total = 0
counts.each_pair{ | key, value |
total += value >= 3? (rules[key][:triples]*key + (value -3)*rules[key][:singles]): value * rules[key][:singles]
}
return total
end
I used the new ruby enumerable method tally
def score(dice)
return 0 if dice.empty?
ans = dice.tally.map do |k,v|
case k
when 1
three = (k * 1000) * (v/3)
val = (v%3) * 100
val + three
when 5
three = (k * 100) * (v/3)
val = (v%3) * 50
val + three
else
(k * 100) * (v/3)
end
end
ans.reduce(0, :+)
end
My attempt, feedback and refactoring suggestions most welcome:
def score(dice)
score = 0
score_array = [[0, 100, 200, 1000, 1100, 1200], [0, 0, 0, 200, 200, 200], [0, 0, 0, 300, 300, 300], [0, 0, 0, 400, 400, 400], [0, 50, 100, 500, 550, 600], [0, 0, 0, 600, 600, 600]]
tally_hash = {1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0}
dice.sort.tally.each do |key, value|
tally_hash[key] += value
end
tally_hash.each do |key, value|
score += score_array[key -1][value]
end
return score
end
I used hash for score
def score(dice)
score_map = {
1 => 100,
5 => 50
}
cluster = dice.inject(Hash.new(0)) {|hash, num| hash[num] += 1; hash}
cluster.inject(0) do |sum, (num, count)|
set_count = count / 3
sum += num == 1 ? 1000 * set_count : num * 100 * set_count
sum + (score_map[num] || 0) * (count % 3)
end
end
def score(dice)
score = 0
dice.uniq.each do |number|
count = dice.count number
weight = if number == 1 then 10 else number end
if count >= 3
score += weight * 100
count -= 3
end
if count > 0 and number == 1 or number == 5
score += count * weight * 10
end
end
score
end
def score(dice)
ones = fives = rest = 0
one_count = dice.count(1)
if one_count > 2
ones = 1000
one_count -= 3
end
ones += one_count * 100
five_count = dice.count(5)
if five_count > 2
fives = 500
five_count -= 3
end
fives += five_count * 50
[2,3,4,6].each do |num|
if dice.count(num) > 2
rest += num * 100
end
end
return ones + fives + rest
end