### Render numeric sequence as array of letters

by gryphon (Abbot)
 on May 19, 2003 at 20:58 UTC Need Help??

gryphon has asked for the wisdom of the Perl Monks concerning the following question:

Greetings fellow monks,

I just finished an interesting coding problem for work. We have an online test-taking & grading system, and while doing some updates for it, I needed an algorithm that renders a numeric sequence in order by letter, regardless of the numbers within the sequence. For example:

5, 100, 2, 8, 40 ...becomes... B, E, A, C, D

After some tinkering, I came up with the following:

```my @original_array = qw(5, 100, 2, 8, 40);
my @sequence;
my @ordered = sort @original_array;

for (my \$y = 0; \$y < scalar @ordered; \$y++) {
for (my \$x = 0; \$x < scalar @ordered; \$x++) {
push @sequence, \$x + 1
if (\$ordered[\$x] == \$original_array[scalar @sequence]);
}
}
my @letter_sequence = map { local \$_ = \$_; tr/1-9/a-h/; \$_ } @sequence
+;

I was wondering if any of you all had encountered such a problem in the past and if so if you have any code for it that's shorter than what I've posted. The above works, as far as I can tell, but I have a sneeking suspicion that there's a much better way to write it. (Oh, and the @original_array will never be bigger than 9 elements.) Thanks.

gryphon
code('Perl') || die;

Replies are listed 'Best First'.
•Re: Render numeric sequence as array of letters
by merlyn (Sage) on May 19, 2003 at 21:19 UTC
It looks like you want ordered rank.
```my @original = qw(5, 100, 2, 8, 40);
my @sorted_indicies = sort { \$original[\$a] <=> \$original[\$b] } 0..\$#or
+iginal;
my @rank;
@rank[@sorted_indicies] = 0..\$#sorted_indicies;
my @letters = map { chr(ord('A') + \$_) } @rank;
print "@letters\n";
That gives me "B E A C D", just like you want.

-- Randal L. Schwartz, Perl hacker
Be sure to read my standard disclaimer if this is a reply.

Re: Render numeric sequence as array of letters
by tilly (Archbishop) on May 19, 2003 at 21:23 UTC
Without testing (no Perl on this machine...), I am willing to bet that your code does not work. 100 is going to sort first because sort is lexigraphical. But here is an alternate solution that should work fine even for long sequences.
```my @original_array = qw(5, 100, 2, 8, 40);

# Build lookup hash
my %num2letter;
my \$char = "A";
foreach my \$num (sort {\$a <=> \$b} @original_array) {
\$num2letter{\$num} = \$char++;
}

my @letter_sequence = @num2letter{@original_array};
(Note - if you are looping over an array searching for something, then you probably wanted a hash...)

Update: Change map to a more efficient slice as suggested by Aristotle, remove accidental space within a my.

Any reason not to use a slice here?
```my @letter_sequence = @num2letter{@original_array};

Makeshifts last the longest.

You are exactly right, there is no reason not to use the more efficient slice operation. I just wasn't thinking that way.
Re: Render numeric sequence as array of letters (index sort + slice)
by tye (Sage) on May 19, 2003 at 21:21 UTC

Try:

```    my @data= ( 5, 100, 2, 8, 40 );
my @indices= sort { \$data[\$a] <=> \$data[\$b] } 0..\$#data;
my @letters;
@letters[@indices]= ('A'..'Z');
print "@letters\n";
so long as you don't have more than 26 numbers.

- tye
Re: Render numeric sequence as array of letters
by Abigail-II (Bishop) on May 19, 2003 at 22:10 UTC
I assume you want B E D A C as output, since B E A C D corresponds with 100 40 5 2 8. I'd use something similar to a Schwartzian Transfer:
```my @original = qw /5 100 2 8 40/;
my \$s        = "A";
my @sorted   = map {\$_ -> [1]}
sort {\$b -> [0] <=> \$a -> [0]}
map {[\$_ => \$s ++]} @original;

Abigail

I took the same approach, with the same assumption, and then realized that "ordered rank" was the objective.

```# Tested code; outputs:
# B E A C D

my @original = (5, 100, 2, 8, 40);
my \$s        = 'A';
my \$n        = 1;
my @sorted   = map  { \$_->[2]             }
sort { \$a->[1] <=> \$b->[1] }
map  { [ @\$_, \$s++ ]       }
sort { \$a->[0] <=> \$b->[0] }
map  { [ \$_, \$n++ ]        }
@original;
print "@sorted\n";

Re: Render numeric sequence as array of letters
by HatMan (Novice) on May 20, 2003 at 02:30 UTC
This has caught a lot of people's imagination! Here's my offering. It's a shame the original array is called 'original_array', otherwise it could have been a lot shorter!
```  my @original_array = qw/ 5 100 2 8 40 /;
my @sequence;

@sequence[
sort {
\$original_array[\$a] <=> \$original_array[\$b]
}(0 .. \$#original_array)
] = 'A' .. 'Z';

print "@sequence\n";
output
```  B E A C D
Rob
Re: Render numeric sequence as array of letters
by BrowserUk (Pope) on May 19, 2003 at 21:24 UTC

Updated sort to numeric!

```sub grade{
my (%h, %i);
@h{ ('A' .. 'I')[0..\$#_] } = @_;
@i{ values %h } = keys %h;
@i{ sort{ \$b <=> \$a } keys %i };
}
my @n=(5, 100, 2, 8, 40);
B E D A C

Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
Re: Render numeric sequence as array of letters
by perlguy (Deacon) on May 19, 2003 at 21:18 UTC

```my \$count = 65; # 65 is the letter 'A' in ASCII
my @original_array = qw(5 100 2 8 40);

my %original_map = map {
\$_ => chr(\$count++)
} sort { \$a <=> \$b } @original_array;

my @letter_sequence = map \$original_map{\$_}, @original_array;
print "@letter_sequence\n";

Update: Good call, merlyn. Here is one to eliminate the duplicates, I think:

```my %seen;
my %original_map = map {
\$_ => chr(\$count++)
} grep !\$seen{\$_}++,
sort { \$a <=> \$b } @original_array;
Re: Render numeric sequence as array of letters
by Not_a_Number (Prior) on May 20, 2003 at 17:35 UTC

Maybe a bit late, but there's an issue in the OP's code that nobody else has explicitly mentioned, namely that the first line:

my @original_array = qw(5, 100, 2, 8, 40);

will actually put the commas in @original_array along with the numbers themselves, as would have been pointed out if -w had been on

In this instance, it doesn’t matter much, since if I understand correctly, perl will uncomplainingly do a numerical sort on strings as long as each begins with what it interprets as a number.

In other cases, however, putting unneeded commas in a qw(list) could lead to some surprises. Try this:

```my @ary = qw ( 1, foo, 2, bar5, 33 );
for ( @ary ) {
#print only the numbers:
print if /^\d+\$/
}

Dave

Re: Render numeric sequence as array of letters
by mce (Curate) on May 20, 2003 at 13:44 UTC
Hi,
This is a nice question for the Perl Minigolf contests.

---------------------------
Dr. Mark Ceulemans
Senior Consultant
BMC, Belgium
Re: Render numeric sequence as array of letters
by Aristotle (Chancellor) on May 21, 2003 at 12:21 UTC
Assuming A-Z will suffice:
```my @original_array = qw(5 100 2 8 40);

my @letter_seq = do {
my %letter_for;
@letter_for{sort { \$a <=> \$b } @original_array} = ('A' .. 'Z');
@letter_for{@original_array};
};

Makeshifts last the longest.

I believe this code has similar problems to my initial posting, which merlyn was quick to point out. If the @original_array contains one duplicate, for example, then the same grade is assigned for both, but one letter will be skipped in the process.

True.
```my @original_array = qw(5 100 2 8 8 40);

my @letter_seq = do {
my %letter_for;
@letter_for{@original_array} = ();
@letter_for{sort { \$a <=> \$b } keys %letter_for} = ('A' .. 'Z');
@letter_for{@original_array};
};

Makeshifts last the longest.

Create A New User
Node Status?
node history
Node Type: perlquestion [id://259291]
Front-paged by grinder
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2020-11-26 01:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?

No recent polls found

Notices?