Indexing system - ruby newbie

A

Adam Groves

Hi there,

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!)
 
D

Dave Burt

Adam said:
Hi there,

I'm trying to write a class which converts a number into letters like
so:
0 => -
1 => A
10 => J
27 => AA

Try this:

n = ""
while(n > 0)
s << ?A + n % 26 - 1
n /= 26
end
n << "-" if n.empty?
s.reverse

Also, your @alphabet is ("a".."z").to_a

Cheers,
Dave
 
R

Ross Bamford

Try this:

n = ""
while(n > 0)
s << ?A + n % 26 - 1
n /= 26
end
n << "-" if n.empty?
s.reverse

Also, your @alphabet is ("a".."z").to_a

This isn't as good as Dave's (it's potentially *lots* slower for a
start) but, well, I'm just an #inject addict really...:

def letter(n)
(n < 1) ? '_' : (1...n).inject("A") { |curr, i| curr.succ }
end

letter(0)
# => "_"
letter(1)
# => "A"
letter(10)
# => "J"
letter(27)
# => "AA"
letter(397)
# => "OG"
 
A

Adam Groves

Hey thanks guys. I'm really enjoying learning ruby - especially because
the ruby community is so helpful.
 
A

Adam Groves

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|
 
E

Edward Faulkner

--SUOF0GtieIMvvwua
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

But I'm stuck here.
=20
(1...n).inject("A") { |curr, i| curr.succ}
=20
I still can't quite get my head around blocks beyond .each do |x|

#inject is one of the basic tools of functional programming. That's
why it seems hard. It's a very different way of thinking. And also
very powerful.

That said, I think this case is a completely gratuitious use of
inject. The tipoff is that the argument "i" is completely ignored.

I like this much better:

def letter(n)
return '_' if n=3D=3D0
n=3D=3D1 ? "A" : letter(n-1).succ
end

regards,
Ed

--SUOF0GtieIMvvwua
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQFEBcKbnhUz11p9MSARAkLZAJ4kUYU2JF1MTukPMl/kE4xoEVM/AACfW4ij
Q1/1uQTDtcyTEaTm0by1FdE=
=YuNn
-----END PGP SIGNATURE-----

--SUOF0GtieIMvvwua--
 
R

Ross Bamford

#inject is one of the basic tools of functional programming. That's
why it seems hard. It's a very different way of thinking. And also
very powerful.

That said, I think this case is a completely gratuitious use of
inject. The tipoff is that the argument "i" is completely ignored.

Well, sorry, I didn't realise we had to use them all. I like to use it
where I want to give back something new from a block, but don't want:

a = []
something.each { |e| a << e end }
a

In this case it was just a snazzier alternative to doing the (n-1).times
and so on...

Did I say I'm an #inject *addict* ?
I like this much better:

def letter(n)
return '_' if n==0
n==1 ? "A" : letter(n-1).succ
end

Well, to each his own, but (maybe this is a bit pathological,
though...):

def letter(n)
(1...n).inject("A") { |curr, i| curr.succ}
end

def letter2(n)
return '_' if n==0
n==1 ? "A" : letter2(n-1).succ
end

p letter(327021)
# => "ROSS"

p letter2(327021)
# => -:3:in `letter2': stack level too deep (SystemStackError)
from -:3:in `letter2'
from -:11
 
R

Ross Bamford

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
 
W

William James

Adam said:
Hi there,

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!)


def letter( n )
return "-" if n == 0

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
 
M

Malte Milatz

Adam said:
I'm trying to write a class which converts a number into letters like
so:
0 => -
1 => A
10 => J
27 => AA

Only after reading the other posts, I realized you want Y, Z, AA, AB, AC
and not Y, Z, AA, BB, CC, which is what is produced by this:

def letter(i)
return '-' if i < 1
('A'..'Z').to_a.at(i%26 - 1) * ((i-1)/26 + 1)
end

Malte
 
R

Rob

Only after reading the other posts, I realized you want Y, Z, AA, AB, AC
and not Y, Z, AA, BB, CC, which is what is produced by this:

def letter(i)
return '-' if i < 1
('A'..'Z').to_a.at(i%26 - 1) * ((i-1)/26 + 1)
end

Malte

could add one method to Integer
and avoid a separate class.
(_to_aa is a helper that works on
0..25 nstead of 1..26)

eg
puts 10.to_aa, (10**1000).to_aa

class Integer

def to_aa # (1..26) to (A..Z)
if (self < 1)
'-'
else
(self-1)._to_aa
end
end

protected
def _to_aa # helper (0..25) to (A..Z)
if (self < 26)
(self+?A).chr
else
((self/26)-1)._to_aa+(self%26)._to_aa
end
end

end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top