Convert excel column letter to integer in Ruby - ruby

What's are the simplest way to convert excel-like column letter to integer?
for example:
AB --> 27
AA --> 26
A --> 0
Z --> 25

def excel_col_index( str )
value = Hash[ ('A'..'Z').map.with_index.to_a ]
str.chars.inject(0){ |x,c| x*26 + value[c] + 1 } - 1
end
Or
def excel_col_index( str )
offset = 'A'.ord - 1
str.chars.inject(0){ |x,c| x*26 + c.ord - offset } - 1
end

I would do something like this:
def column_name_to_number(column_name)
multipliers = ('A'..'Z').to_a
chars = column_name.split('')
chars.inject(-1) { |n, c| multipliers.index(c) + (n + 1) * 26 }
end

here's a version of the accepted answer, with a spec:
RSpec.describe "#excel_col_index" do
def excel_col_index(str)
value = Hash[('A'..'Z').map.with_index.to_a]
str.chars.inject(0) { |x, c| x * 26 + value[c] + 1 } - 1
end
{ "A" => 0, "Z" => 25, "AB" => 27, "AA" => 26 }.each do |col, index|
it "generates #{index} from #{col}" do
expect(excel_col_index(col)).to eq(index)
end
end
end
(but I'll also edit the accepted answer to have the required - 1)

ah nevermind..
def cell2num col
val = 0
while col.length > 0
val *= 26
val += (col[0].ord - 'A'.ord + 1)
col = col[1..-1]
end
return val - 1
end

This is one of those bits that you can keep iterating on for a long time. I ended up with this:
"AB1".each_codepoint.reduce(0) do |sum, n|
break sum - 1 if n < 'A'.ord # reached a number
sum * 26 + (n - 'A'.ord + 1)
end # => 27
From the xsv source code

Related

Algorithm Challenge number formatting problem

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

I have a issue.. Appending

I have this code:
1 #!/local/usr/bin/ruby
2
3 users = (1..255).to_a
4
5 x = " "
6 y = " "
7 z = " "
8 #a = " "
9
10 count = 1
11 users.each do |i|
12 x << i if count == 1
13 y << i if count == 2
14 z << i if count == 3
15 # if x.length == 60
16 # a << i if count == 1
17 # a << i if count == 2
18 # a << i if count == 3
19 # else
20 # end
21 if count == 3
22 count = 1
23 else
24 count += 1
25 end
26 end
27
28 puts x.length
29 puts y.length
30 puts z.length
31 #puts a.length
32
What this code does is append The numbers 1-255 into three different strings and outputs how many numbers are in each string.
IT WORKS
Example of working code:
[user#server ruby]$ ruby loadtest.rb
86
86
86
[user#server ruby]$
Now what I want it to do is have a failsafe called a as seen above, commented out, What I want is this, if each string contains 60 numbers I want it to append into the a string until there are no more numbers.
When I try to do it with the commented out section it outputs this:
[user#server ruby]$ ruby loadtest.rb
86
86
86
4
[user#server ruby]$ ruby loadtest.rb
WHY?! What am I doing wrong?
What this code does is append The numbers 1-255 into three different strings and outputs how many numbers are in each string.
After reducing the number of values being iterated for readability, here's what it's doing:
users = (1..5).to_a
x = " "
y = " "
z = " "
count = 1
users.each do |i|
x << i if count == 1 # => " \u0001", nil, nil, " \u0001\u0004", nil
y << i if count == 2 # => nil, " \u0002", nil, nil, " \u0002\u0005"
z << i if count == 3 # => nil, nil, " \u0003", nil, nil
if count == 3
count = 1
else
count += 1
end
end
x # => " \u0001\u0004"
y # => " \u0002\u0005"
z # => " \u0003"
puts x.length
puts y.length
puts z.length
# >> 3
# >> 3
# >> 2
Your code is creating binary inside the strings, not "numbers" as we normally think of them, as digits.
Moving on, you can clean up your logic using each_with_index and case/when. To make the results more readable I switched from accumulating into strings into arrays:
users = (1..5).to_a
x = []
y = []
z = []
users.each_with_index do |i, count|
case count % 3
when 0
x << i
when 1
y << i
when 2
z << i
end
end
x # => [1, 4]
y # => [2, 5]
z # => [3]
puts x.length
puts y.length
puts z.length
# >> 2
# >> 2
# >> 1
The real trick in this is the use of %, which does a modulo on the value.
... if each string contains 60 numbers I want it to append into the a string until there are no more numbers
As written, you are unconditionally appending to x,y,z even after they hit your limit.
You need to add a conditional around this code:
x << i if count == 1
y << i if count == 2
z << i if count == 3
so that it stops appending once it hits your limit.
By the looks of the else block that does nothing, I think you were headed in that direction:
if x.length == 60
a << i if count == 1
a << i if count == 2
a << i if count == 3
else
x << i if count == 1
y << i if count == 2
z << i if count == 3
end
Even that, though, won't do exactly what you want.
You'll want to check the string you are appending to to see if it has hit your limit yet.
I'd suggest refactoring to make it cleaner:
users.each do |i|
target_string = case count
when 1 then x
when 2 then y
when 3 then z
end
target_string = a if target_string.length == 60
target_string << i
if count == 3
count = 1
else
count += 1
end
end
It may be better to use an array instead of string as you are pushing numbers into those variables.
Let me propose a solution which achieves more or less what you are trying to do, but uses few Ruby tricks that may be useful in future.
x, y, z = r = Array.new(3) {[]}
a = []
iter = [0,1,2].cycle
(1..255).each do |i|
r.all? {|i| i.size == 60} ? a << i : r[iter.next] << i
end
p x.size, y.size, z.size
p a.size
Let's define our arrays. Even though I have arrays x, y, and z, they are there only because they were present in your code - I think we just need three arrays, each of which would collect numbers as they are picked from a range of numbers - between 1 to 255 - one by one. x,y,z = r uses parallel assignment technique and is equivalent to x,y,z = r[0],r[1],r[2]. Also, use of Array.new(3) {[]} helps in creating the Array of Array such that when we access r[1] it is initialized with empty array([]) by default.
x, y, z = r = Array.new(3) {[]}
a = []
In order to determine which array the next number picked from range has to be placed in, we will use an Enumerator generated from Enumerable#cycle. This enumerator is special - because it is soft of infinite in nature - and we can keep asking it to give an element by calling next, and it will cycle through the array elements of [0,1,2] - returning us 0,1,2,0,1,2,0,1,2... infinitely.
iter = [0,1,2].cycle
Next, we will iterate through the range of numbers 1..255. During each iteration, we will check whether all the 3 arrays in which we are collecting number have desired size of 60 with the help of Enumerable#all? - if so, we will append the number to array a - else we will assign it to one of the sub arrays of r based on the array index returned by iter enumerator.
(1..255).each do |i|
r.all? {|i| i.size == 60} ? a << i : r[iter.next] << i
end
Finally, we print the size of each of the array.
p x.size, y.size, z.size
#=> 60, 60, 60
p a.size
#=> 75

very simple ruby programing, getting error and don't understand it

I'm asked to write the ruby program that generate the output based the given command,
The full description
I'm really new in ruby (maybe few hours that I have started ruby)
I'm getting this error, please check my code for other possible errors:
Thank you.
n `block in each2': undefined method `[]' for #<MyVector:0x00000002c4ad90 #array=[2, 3, 4]> (NoMethodError)
What I have done so far:
# MyVector Class
class MyVector
def initialize (a)
if !(a.instance_of? Array)
raise "ARGUMENT OF INITIALIZER MUST BE AN ARRAY"
else
#array = a
end
end
def array
#array
end
def to_s
#array.to_s
end
def length
#array.length
end
def each2(a)
raise Error, "INTEGER IS NOT LIKE VECTOR" if a.kind_of?(Integer)
Vector.Raise Error if length != a.length
return to_enum(:each2, a) unless block_given?
length.times do |i|
yield #array[i], a[i]
end
self
end
def * (a)
Vector.Raise Error if length != a.length
p = 0
each2(a) {|a1, a2|p += a1 * a2}
p
end
end
# MyMatrix Class
class MyMatrix
def initialize a
#array=Array.new(a.length)
i=0
while(i<a.length)
#array[i]=MyVector.new(a[i])
end
end
def to_s
#array.to_s
end
def transpose
size=vectors[0].length
arr= Array.new(size)
i=0
while i<size
a=Array.new(vector.length)
j=0
while j<a.length
a[j]=vectors[j].arr[i]
j+=1
end
arr[i]=a
i+=1
end
arr[i]=a
i+=1
end
def *m
if !(m instance_of? MyMatrix)
raise Error
a=Array.new(#array.length)
i=0
while (i<#array.length)
a[i]=#array[i]*m
i=i+1
end
end
end
end
Input:
Test code
v = MyVector.new([1,2,3])
puts "v = " + v.to_s
v1 = MyVector.new([2,3,4])
puts "v1 = " + v1.to_s
puts "v * v1 = " + (v * v1).to_s
m = MyMatrix.new([[1,2], [1, 2], [1, 2]])
puts "m = " + m.to_s + "\n"
puts "v * m = " + (v * m).to_s
m1 = MyMatrix.new([[1, 2, 3], [2, 3, 4]])
puts "m1 = " + m1.to_s + "\n"
puts "m * m1 = " + (m * m1).to_s
puts "m1 * m = " + (m1 * m).to_s
Desired Output:
v = 1 2 3
v1 = 2 3 4
v * v1 = 20
m =
1 2
1 2
1 2
v * m = 6 12
m1 =
1 2 3
2 3 4
m * m1 =
5 8 11
5 8 11
5 8 11
m1 * m =
6 12
9 18
length.times do |i|
yield #array[i], a[i]
end
In the above block, a is an instance of MyVector. You need to define the [] operator on it, probably something like:
def [](i)
#array[i]
end

adding 1 to each element of an array

i tred creating a method called "Sum_plus_one" that accepts an array argument containing integers. Themethod should return the sum of the integers in the array after adding one to each of them.
example:
sum_plus_one([1,2,3])
result should be: 9
my code looks like this
def sum_plus_one(*nums)
for num in nums
num + 1
end
total = 0
for num in nums
total += num
end
return total
end
Why not do a little bit of math beforehand and see that summing the array-elements-plus-one is the same as summing the elements and then adding the array length? For example:
(5+1) + (6+1) + (11+1) = 5 + 6 + 11 + (1 + 1 + 1)
= 5 + 6 + 11 + 3
That gives you something nice and simple:
array.inject(:+) + array.length
map/reduce is handy here:
def sum_plus_one(nums)
nums.map(&:succ).reduce(:+)
end
Edit:
here is one way to make your code work:
def sum_plus_one(nums)
nums.map! do |num|
num + 1
end
total = 0
for num in nums
total += num
end
return total
end
Functional Style Version
[1, 2, 3].reduce(0) {|acc, n| acc + n + 1}
Use Enumerable#inject
[105] pry(main)> arr
=> [1, 2, 3]
[106] pry(main)> arr.inject(0) { |var, i| var + i + 1 }
=> 9
So the method would look like
def sum_plus_one(*nums)
nums.inject(0) { |var, num| var + num + 1 }
end
your problem is that you need to assign your num + 1 value to the corresponding element of the array that you are enumerating in the first loop.
maybe something like this:
for i in (0...nums.count)
nums[i] += 1
end
after that your program should work
(yes, you can use fancy libraries or constructs instead of the above loop)
please note that you can eliminate the top loop and just add num + 1 to your total in the second loop.

Better way to write this in Ruby?

I'm new to Ruby. After a ton of refactoring I came down to this. Is there a better way to write this?
51 def tri_num?(n)
52 i = 1
53 while i < n
54 return i if i * (i + 1) / 2 == n
55 i += 1
56 end
57 raise InvalidTree
58 end
What about solving it directly?
def tri_num? n
i = (0.5*(-1.0 + Math.sqrt(1.0 + 8.0*n))).to_i
if i*(i+1)/2 == n
return i
else
raise InvalidTree
end
end
Though I don't know if tri_num? is a good name. Usually a function ending with a ? should return true or false.
Yes.
def tri_num?(n)
1.upto(n-1) do |i|
return i if i * (i + 1) / 2 == n
end
raise InvalidTree
end
I thought the same as dantswain, basically invert the equation:
=> i * (i + 1) / 2 = n
=> i * (i + 1) = 2*n
=> i^2 + i = 2*n
=> i^2 + i -2*n = 0
And the solutions for the above are:
i = (-1 +- sqrt(1+8n))/2
Here I don't consider the - solution as it will give negative for any value of n bigger than 0, in the end the code is:
def tri_num?(n)
i = (-1 + Math.sqrt(1 + 8*n))/2.0
return i.to_i if i == i.to_i
raise InvalidTree
end
def tri_num?(n)
(1...n).each do |i|
return i if i * (i + 1) / 2 == n
end
rails InvalidTree # not defined..
end

Resources