2

I recently started learning Perl, so I'm not too familiar with the functions and syntax. If I have a Perl array and some variables,

#!/usr/bin/perl
use strict;
use warnings;
my @numbers = (a =>1, b=> 2, c => 3, d =>4, e => 5);
my $x;
my $range = 5;
$x = int(rand($range));
print "$x";

to generate a random number between 1-5, how can I get the program to print the actual key (a, b, c, etc.) instead of just the number (1, 2, 3, 4, 5)?

Slippy
  • 33
  • 6
  • 1
    rand(5) will return a number from 0 to 4.9999...; int makes it an integer from 0-4 – ysth Oct 15 '17 at 18:01
  • This might be a helpful resource: https://perldoc.perl.org/perlintro.html – martin clayton Oct 15 '17 at 18:04
  • The keys (indexes) of `@numbers` are `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8` and `9`. `a`, `b` and `c` aren't even valid keys (indexes) for an array. – ikegami Oct 16 '17 at 02:16
  • Why not using `qw(a b c d e)[int(rand(5))]`? Items in an array have an index already; if you want an individual index, you might want to use a hash instead: `{1=> 'a', 2=> 'b', 3 => 'c', 4 => 'd', 5 => 'e'}->{int(1 + rand(5))}` – U. Windl Aug 01 '23 at 07:40

3 Answers3

0

There's many ways to do it. For example, declare "numbers" as a hash rather than an array. Note that the keys come first in each key-value pair, and here you want to use your random int as the key:

my %numbers = ( 0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e' );

Then you can look up the "key" as you call it using:

my $key = $numbers{$x};

Note that rand( $x ); returns a number greater than or equal to zero and less than $x. So if you want integers in the range 1-5, you must add 1 in your code: at the moment you'll get 0-4, not 1-5.

martin clayton
  • 76,436
  • 32
  • 213
  • 198
  • It's not working for me when I try your code. Do I need to convert the hash into an array first? – Slippy Oct 15 '17 at 19:48
0

It seems that you want to do a reverse lookup, key-by-value, opposite to what we get from a hash. Since a hash is a list you can reverse it and use the resulting hash to look up by number.

A couple of corrections: you need a hash variable (not an array), and you need to add 1 to your rand integer generator so to have the desired 1..5 range

use warnings;
use strict;
use feature 'say';

my %numbers = (a => 1, b => 2, c => 3, d => 4, e => 5);

my %lookup_by_number = reverse %numbers;  # values need be unique

my $range = 5;
my $x = int(rand $range) + 1;

say $lookup_by_number{$x};

Without reversing the hash you'd need to iterate the hash %numbers over values, testing each against $x so to find its key.

If there are same values for various keys in your original hash then you have to do it by hand since reverse-ing would attempt to create a hash with duplicate keys, in which case only the last one assigned remains. So you'd lose some values. One way

my @at_num = grep { $x == $numbers{$_} } keys %numbers;

as in the post that this was marked as duplicate of.


But then you should build a data structure for reverse lookup so to not search through the list every time information is needed. This can be a hash where keys are the list of unique numbers while their values are then array references (arrayrefs) with corresponding keys from the original hash

use warnings;
use strict;

my %num = (a => 1, b => 2, c => 1, d => 3, e => 2);  # with duplicate values

my %lookup_by_num;

foreach my $key (keys %num) {
    push @{ $lookup_by_num{$num{$key}} }, $key;
}

say "$_ => [ @{$lookup_by_num{$_}} ]" for keys %lookup_by_num;

This prints

1 => [ c a ]
3 => [ d ]
2 => [ e b ]

A nice way to display complex data structures is via Data::Dumper, or Data::Dump (or others).

The expression @{ $lookup_by_num{ $num{$key} } } extracts the value of %lookup_by_num for the key $num{$key}and dereferences it @{ ... }, so that it can then push the $key to it. The critical part of this is that the first time it encounters $num{$key} it autovivifies the arrayref and its corresponding key. See this post with its references for details.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • @Slippy I added discussion of a more complex case, when values of some keys are the same. – zdim Oct 16 '17 at 17:28
0

Firstly, arrays don't have keys (well, they kind of do, but they're integers and not the values you want). So I think you want a hash, not an array.

my %numbers = (a =>1, b=> 2, c => 3, d =>4, e => 5);

And if you want to get the letter, given the integer then you need the reverse of this hash:

my %rev_numbers = %numbers;

Note that reversing a hash like this only works if the values in your original hash are unique (because reversing a hash makes the values into keys and hash keys are always unique).

Then, you can just look up an integer in your %rev_hash to get its associated letter.

my $integer = 3;
say $rev_numbers{$integer}; # prints 'c'
Dave Cross
  • 68,119
  • 3
  • 51
  • 97