Alexx
Alexx

Reputation: 475

Can anyone explain how to print all keys if two keys are same but with different values?

In this code two keys "39" are same name but different values , I want to print both keys

use strict; 
use warnings; 

my %studentnames = ( 
14 => Martha, 
27 =>Vivek, 
31 =>Era, 
16 =>Marty, 
25 =>Jason, 
29 =>Socrates, 
19 =>Uri, 
39 =>Nitin , 
39 =>Plato, 
); 

foreach my $name (sort keys %studentnames) 
{ 
    printf "%-8s %s\n", $name, $studentnames{$name};
} 

I am getting error.

Bareword "Martha" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Vivek" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Era" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Marty" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Jason" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Socrates" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Uri" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Nitin" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Plato" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.

Expected Output

14   Martha
27   Vivek 
31   Era
16   Marty 
25   Jason 
29   Socrates 
19   Uri
39   Nitin 
39   Plato

Can anyone tell me how to do it?

Upvotes: 9

Views: 190

Answers (4)

Timur Shtatland
Timur Shtatland

Reputation: 12405

In addition to excellent answers provided by others, and explanations of how to make the code work (e.g., quote the names, etc), here is another simple solution. I assume that the student names are unique (as they indeed appear in your example). In this case, use the reverse of your hash. That is, change it from the mapping of number to name to the mapping of name to number. Sort the hash by values (student numbers) numerically, then by keys (student names) ASCIIbetically. This is my guess as to one possible way of sorting that makes intuitive sense to the user.

#!/usr/bin/env perl

use strict;
use warnings;

my %student_name_to_num =
    reverse ( 
        14 => 'Martha', 
        27 => 'Vivek', 
        31 => 'Era', 
        16 => 'Marty', 
        25 => 'Jason', 
        29 => 'Socrates', 
        19 => 'Uri', 
        39 => 'Nitin', 
        39 => 'Plato', 
    ); 

foreach my $name ( sort {
    $student_name_to_num{$a} <=> $student_name_to_num{$b} ||
        $a cmp $b
} keys %student_name_to_num ) { 
    printf "%-8s %s\n", $student_name_to_num{$name}, $name;
} 

Output:

14       Martha
16       Marty
19       Uri
25       Jason
27       Vivek
29       Socrates
31       Era
39       Nitin
39       Plato

Note that the order of the records is different from the sorted order you showed. But it is not clear how you want the records to be sorted (see also my comment under the question).

Upvotes: 2

tobyink
tobyink

Reputation: 13664

This is kinda close:

use strict;
use warnings;
use Tie::Hash::MultiValueOrdered;

tie my %studentnames, 'Tie::Hash::MultiValueOrdered';
%studentnames = (
    14 => 'Martha',
    27 => 'Vivek',
    31 => 'Era',
    16 => 'Marty',
    25 => 'Jason',
    29 => 'Socrates',
    19 => 'Uri',
    39 => 'Nitin',
    39 => 'Plato',
); 

tied(%studentnames)->fetch_list;

while ( my ( $key, $value ) = each %studentnames ) {
    print "$key => @$value\n";
}

But really you want to use a different data structure. Perhaps an array of arrayrefs?

use strict;
use warnings;

my @students = (
    [ 14 => 'Martha'   ],
    [ 27 => 'Vivek'    ],
    [ 31 => 'Era'      ],
    [ 16 => 'Marty'    ],
    [ 25 => 'Jason'    ],
    [ 29 => 'Socrates' ],
    [ 19 => 'Uri'      ],
    [ 39 => 'Nitin'    ],
    [ 39 => 'Plato'    ],
); 

for my $student ( @students ) {
    my ( $num, $name ) = @$student;
    print "$num => $name\n";
}

Or an array of hashrefs:

use strict;
use warnings;

my @students = (
    { num => 14 , name => 'Martha'   },
    { num => 27 , name => 'Vivek'    },
    { num => 31 , name => 'Era'      },
    { num => 16 , name => 'Marty'    },
    { num => 25 , name => 'Jason'    },
    { num => 29 , name => 'Socrates' },
    { num => 19 , name => 'Uri'      },
    { num => 39 , name => 'Nitin'    },
    { num => 39 , name => 'Plato'    },
); 

for my $student ( @students ) {
    print "$student->{num} => $student->{name}\n";
}

Or a hash of arrayrefs:

use strict;
use warnings;

my %students = (
    14 => [ 'Martha'   ],
    27 => [ 'Vivek'    ],
    31 => [ 'Era'      ],
    16 => [ 'Marty'    ],
    25 => [ 'Jason'    ],
    29 => [ 'Socrates' ],
    19 => [ 'Uri'      ],
    39 => [ 'Nitin', 'Plato' ],
); 

for my $key ( sort keys %students ) {
    for my $name ( @{$students{$key}} ) {
        print "$key => $name\n";
    }
}

Or you could even create a lightweight "person" class.

use Z;

my $Person = class sub {
    has num  => ( type => PositiveInt );
    has name => ( type => NonEmptyStr );
};

my @students = (
    $Person->new( num => 14, name => 'Marta'    ),
    $Person->new( num => 27, name => 'Vivek'    ),
    $Person->new( num => 31, name => 'Era'      ),
    $Person->new( num => 16, name => 'Marty'    ),
    $Person->new( num => 25, name => 'Jason'    ),
    $Person->new( num => 29, name => 'Socrates' ),
    $Person->new( num => 19, name => 'Uri'      ),
    $Person->new( num => 39, name => 'Nitin'    ),
    $Person->new( num => 39, name => 'Plato'    ),
); 

for my $student ( @students ) {
    printf "%s => %s\n", $student->num, $student->name;
}

There's a lot of ways to go about solving this, but a single flat hash of strings is probably not one of them.

Upvotes: 6

GMB
GMB

Reputation: 222622

As a starter: the hash values are strings, so they need to be quoted. This is why you are getting a syntax error:

my %studentnames = ( 
    14 => 'Martha', 
    27 => 'Vivek', 
    31 => 'Era', 
    ... 
); 

Then: there is a misconception of what a Perl hash is. Each key in the hash must be unique. Perl tolerates declaring a hash with duplicate keys, but under the hood, only the last value of each key is retained.

So this:

my %studentnames = ( 
    14 => 'Martha', 
    39 => 'Nitin', 
    39 => 'Plato' 
); 

Is equivalent to:

my %studentnames = ( 
    14 => 'Martha', 
    39 => 'Plato' 
); 

Another way to see it is to put the assignments in separate instructions:

my %studentnames;
$studentnames{14} = 'Martha';
$studentnames{39} = 'Nitin';
$studentnames{39} = 'Plato';

print $studentnames{39}, "\n";
# Plato

Upvotes: 4

Quentin
Quentin

Reputation: 944175

Two keys cannot be the same. One will overwrite the other. If you want to have multiple values for one key then you need to design your data structure to support that (e.g. by having the value be an arrayref).

Your error messages are unrelated to that problem (you forgot to put quotes around your string values).

Upvotes: 9

Related Questions