I'm trying to write a class which converts a number into letters like
so:
0 => -
1 => A
10 => J
27 => AA
... ad infinitum. My class looks like this at the moment (please don't
laugh!)
def letter(number) @index = number - 1
if @index == 0
return "-"
end @index_string = ""
@index_array= []
while @index > 0 do @remainder = @index%27 @index_array << @remainder @index = @index/27
end @index_array @index_array.each do |i|
# I'm sure there's a better way to do this @alphabet =
["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] @index_string << @alphabet[i-1]
end
return @index_string.reverse!
end
This works fine for the first round: 26 returns "Z". But 27 returns "AZ"
because @index_array is [0,1]. I'd appreciate any help (and tips on how
to write tighter code!)
I'm trying to write a class which converts a number into letters like
so:
0 => -
1 => A
10 => J
27 => AA
.. ad infinitum. My class looks like this at the moment (please don't
laugh!)
def letter(number) @index = number - 1
if @index == 0
return "-"
end @index_string = ""
@index_array=
while @index > 0 do @remainder = @index%27 @index_array << @remainder @index = @index/27
end @index_array @index_array.each do |i|
# I'm sure there's a better way to do this @alphabet =
["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] @index_string << @alphabet[i-1]
end
return @index_string.reverse!
end
This works fine for the first round: 26 returns "Z". But 27 returns "AZ"
because @index_array is [0,1]. I'd appreciate any help (and tips on how
to write tighter code!)
result = n.to_s(27)
i = 0
while i < result.size - 1
inc = result[-i-2,1].to_i(27)
inc += 1 if inc + result[-1,1].to_i(27) > 26
n += 27**i * inc
result = n.to_s(27)
i += 1
end
result.tr(((1..9).to_a + ('a'..'q').to_a).join,
('A'..'Z').to_a.join )
end
Inject is real easy, and very handy. It's just like 'each', except it
also allows the result of the previous iteration to be injected via the
first argument. For the first iteration, you provide the initial result.
For example:
a = [1,2,3,4,5]
a.inject(0) { |sum, i| sum + i }
# => 15
What happens is:
Block is called with sum = 0, i = 1
Block returns 1
Block is called with sum = 1, i = 2
Block returns 3
Block is called with sum = 3, i = 3
Block returns 6
Block is called with sum = 6, i = 4
Block returns 10
Block is called with sum = 10, i = 5
Block returns 15
No more elements, so inject returns 15.
In Ruby, inject allows you to omit the initial value, in which case the
first _two_ elements from the enumerable are passed to the first
iteration, with things proceeding as above from there, so I could have
written:
a.inject { |sum, i| sum + i }
And would have:
Block is called with sum = 1, i = 2
Block returns 3
Block is called with sum = 3, i = 3
Block returns 6
.
.
etc.
You can use inject for much more than just summing stuff up. Comes in
very handy for these cryptic one-lines (even if you have to 'misuse' it
a bit occasionally).
···
On Thu, 2006-03-02 at 00:14 +0900, Adam Groves wrote:
Dear Ross,
it works a treat but I'm having a bit of trouble figuring out what's
going on.
--- Ursprüngliche Nachricht ---
Von: Ross Bamford <rossrt@roscopeco.co.uk>
An: ruby-talk@ruby-lang.org (ruby-talk ML)
Betreff: Re: Indexing system - ruby newbie
Datum: Thu, 2 Mar 2006 01:37:06 +0900
On Thu, 2006-03-02 at 00:14 +0900, Adam Groves wrote:
> Dear Ross,
>
> it works a treat but I'm having a bit of trouble figuring out what's
> going on.
>
> (n < 1) ? '_' : (1...n).inject("A") { |curr, i| curr.succ }
>
> I get this:
> if n<1
> '_'
> else
>
> But I'm stuck here.
>
> (1...n).inject("A") { |curr, i| curr.succ}
>
> I still can't quite get my head around blocks beyond .each do |x|
Inject is real easy, and very handy. It's just like 'each', except it
also allows the result of the previous iteration to be injected via the
first argument. For the first iteration, you provide the initial result.
For example:
a = [1,2,3,4,5]
a.inject(0) { |sum, i| sum + i }
# => 15
What happens is:
Block is called with sum = 0, i = 1
Block returns 1
Block is called with sum = 1, i = 2
Block returns 3
Block is called with sum = 3, i = 3
Block returns 6
Block is called with sum = 6, i = 4
Block returns 10
Block is called with sum = 10, i = 5
Block returns 15
No more elements, so inject returns 15.
In Ruby, inject allows you to omit the initial value, in which case the
first _two_ elements from the enumerable are passed to the first
iteration, with things proceeding as above from there, so I could have
written:
a.inject { |sum, i| sum + i }
And would have:
Block is called with sum = 1, i = 2
Block returns 3
Block is called with sum = 3, i = 3
Block returns 6
.
.
etc.
You can use inject for much more than just summing stuff up. Comes in
very handy for these cryptic one-lines (even if you have to 'misuse' it
a bit occasionally).
--- Ursprüngliche Nachricht ---
Von: Ross Bamford <rossrt@roscopeco.co.uk>
An: ruby-talk@ruby-lang.org (ruby-talk ML)
Betreff: Re: Indexing system - ruby newbie
Datum: Thu, 2 Mar 2006 01:37:06 +0900
On Thu, 2006-03-02 at 00:14 +0900, Adam Groves wrote:
> Dear Ross,
>
> it works a treat but I'm having a bit of trouble figuring out what's
> going on.
>
> (n < 1) ? '_' : (1...n).inject("A") { |curr, i| curr.succ }
>
> I get this:
> if n<1
> '_'
> else
>
> But I'm stuck here.
>
> (1...n).inject("A") { |curr, i| curr.succ}
>
> I still can't quite get my head around blocks beyond .each do |x|
Inject is real easy, and very handy. It's just like 'each', except it
also allows the result of the previous iteration to be injected via the
first argument. For the first iteration, you provide the initial result.
For example:
a = [1,2,3,4,5]
a.inject(0) { |sum, i| sum + i }
# => 15
What happens is:
Block is called with sum = 0, i = 1
Block returns 1
Block is called with sum = 1, i = 2
Block returns 3
Block is called with sum = 3, i = 3
Block returns 6
Block is called with sum = 6, i = 4
Block returns 10
Block is called with sum = 10, i = 5
Block returns 15
No more elements, so inject returns 15.
In Ruby, inject allows you to omit the initial value, in which case the
first _two_ elements from the enumerable are passed to the first
iteration, with things proceeding as above from there, so I could have
written:
a.inject { |sum, i| sum + i }
And would have:
Block is called with sum = 1, i = 2
Block returns 3
Block is called with sum = 3, i = 3
Block returns 6
.
.
etc.
You can use inject for much more than just summing stuff up. Comes in
very handy for these cryptic one-lines (even if you have to 'misuse' it
a bit occasionally).
On Thu, 2006-03-02 at 01:42 +0900, Peter Ertl wrote:
> --- Ursprüngliche Nachricht ---
> Von: Ross Bamford <rossrt@roscopeco.co.uk>
> An: ruby-talk@ruby-lang.org (ruby-talk ML)
> Betreff: Re: Indexing system - ruby newbie
> Datum: Thu, 2 Mar 2006 01:37:06 +0900
>
> On Thu, 2006-03-02 at 00:14 +0900, Adam Groves wrote:
> > Dear Ross,
> >
> > it works a treat but I'm having a bit of trouble figuring out what's
> > going on.
> >
> > (n < 1) ? '_' : (1...n).inject("A") { |curr, i| curr.succ }
> >
> > I get this:
> > if n<1
> > '_'
> > else
> >
> > But I'm stuck here.
> >
> > (1...n).inject("A") { |curr, i| curr.succ}
> >
> > I still can't quite get my head around blocks beyond .each do |x|
>
> Inject is real easy, and very handy. It's just like 'each', except it
> also allows the result of the previous iteration to be injected via the
> first argument. For the first iteration, you provide the initial result.
>
> For example:
>
> a = [1,2,3,4,5]
>
> a.inject(0) { |sum, i| sum + i }
> # => 15
>
> What happens is:
>
> Block is called with sum = 0, i = 1
> Block returns 1
> Block is called with sum = 1, i = 2
> Block returns 3
> Block is called with sum = 3, i = 3
> Block returns 6
> Block is called with sum = 6, i = 4
> Block returns 10
> Block is called with sum = 10, i = 5
> Block returns 15
> No more elements, so inject returns 15.
>
> In Ruby, inject allows you to omit the initial value, in which case the
> first _two_ elements from the enumerable are passed to the first
> iteration, with things proceeding as above from there, so I could have
> written:
>
> a.inject { |sum, i| sum + i }
>
> And would have:
>
> Block is called with sum = 1, i = 2
> Block returns 3
> Block is called with sum = 3, i = 3
> Block returns 6
> .
> .
> etc.
>
> You can use inject for much more than just summing stuff up. Comes in
> very handy for these cryptic one-lines (even if you have to 'misuse' it
> a bit occasionally).
>
> --
> Ross Bamford - rosco@roscopeco.REMOVE.co.uk
>
>
--
This email has been verified as Virus free
Virus Protection and more available at http://www.plus.net
And now for the over-engineered approach to balance out the golfing <g>:
% cat indexer.rb
class Indexer
def initialize @index_cache = ('A'..'Z').to_a @index_cache.unshift('-')
end
def alpha_index(i)
if res = @index_cache[i]
res
else @index_cache[i] = alpha_index(i - 1).succ
end
end
alias alpha_index
end
if $0 == __FILE__
idx = Indexer.new
puts idx.alpha_index(27)
puts idx.alpha_index(0)
puts idx[26]
end
A different approach from others in this thread... no memoization, no
math in the method (let to_s(base) handle it), and a loop that only
runs, at most, as many times as the length of the resulting string.
The idea is, change to base 26, then "uncarry" the ones. Should be
fast.
def letterize(num)
b26 = num.to_s(26).tr("0-9a-p","@-Y")
while b26.sub!(/.@/) { |s| (s[0]-1).chr + "Z" }
end
b26.sub!(/^@/, "")
b26 << "-" if b26.empty?
b26
end
···
On 3/1/06, Logan Capaldo <logancapaldo@gmail.com> wrote:
On Mar 1, 2006, at 12:43 PM, Adam Shelly wrote:
>> OP wanted:
>>
>> ... 24 25 26 27 28 29 ...
>> ... 'X', 'Y', 'Z', 'AA', 'AB', 'AC' ...
>
> Here's one that uses succ to do the dirty work, but doesn't complicate
> things with inject:
> (note that '@'.succ = 'A')
>
> def letter n
> l='@'
> n.times{l.succ!}
> l.gsub(/@/,'-')
> end
>
> -Adam
>
And now for the over-engineered approach to balance out the golfing <g>:
% cat indexer.rb
class Indexer
def initialize @index_cache = ('A'..'Z').to_a @index_cache.unshift('-')
end
def alpha_index(i)
if res = @index_cache[i]
res
else @index_cache[i] = alpha_index(i - 1).succ
end
end
alias alpha_index
end
if $0 == __FILE__
idx = Indexer.new
puts idx.alpha_index(27)
puts idx.alpha_index(0)
puts idx[26]
end
Here's one that uses succ to do the dirty work, but doesn't complicate
things with inject:
(note that '@'.succ = 'A')
def letter n
l='@'
n.times{l.succ!}
l.gsub(/@/,'-')
end
-Adam
And now for the over-engineered approach to balance out the golfing <g>:
% cat indexer.rb
class Indexer
def initialize @index_cache = ('A'..'Z').to_a @index_cache.unshift('-')
end
def alpha_index(i)
if res = @index_cache[i]
res
else @index_cache[i] = alpha_index(i - 1).succ
end
end
alias alpha_index
end
if $0 == __FILE__
idx = Indexer.new
puts idx.alpha_index(27)
puts idx.alpha_index(0)
puts idx[26]
end
% ruby indexer.rb
AA
-
Z
A different approach from others in this thread... no memoization, no
math in the method (let to_s(base) handle it), and a loop that only
runs, at most, as many times as the length of the resulting string.
The idea is, change to base 26, then "uncarry" the ones. Should be
fast.
def letterize(num)
b26 = num.to_s(26).tr("0-9a-p","@-Y")
while b26.sub!(/.@/) { |s| (s[0]-1).chr + "Z" }
end
b26.sub!(/^@/, "")
b26 << "-" if b26.empty?
b26
end