2

I'm learning Perl. I have been able to use grep on an array and the syntax is very simple, like this example:

use strict; use warnings;

my @names = qw(Foo Bar Baz);
my $visitor = <STDIN>;
chomp $visitor;
if (grep { $visitor eq $_ } @names) {
   print "Visitor $visitor is in the guest list\n";
} else {
   print "Visitor $visitor is NOT in the guest list\n";
}

However, I would like to know if there is an equally simple way to use grep on a hash without coding a loop to iterate through each item in the hash.

Here is some example data with the structure I'm working with. Before assigning a URI, I want to check if any item has that uri value already. For example, I want to assign ww1.example.com to item v2rbz1568, but only if no other item has a uri value of ww1.example.com. How can I efficiently accomplish that in Perl?

{
    "0y7vfr1234": {
        "username": "[email protected]",
        "password": "some-random-password123",
        "uri": "ww1.example.com",
        "index": 14
    },
    "v2rbz1568": {
        "username": "[email protected]",
        "password": "some-random-password125",
        "uri": "ww3.example.com",
        "index": 29
    },
    "0zjk1156": {
        "username": "[email protected]",
        "password": "some-random-password124",
        "uri": "ww2.example.com",
        "index": 38
    }
}

I'm using perl 5, version 30 on Linux.

1
  • you can of course use grep with values: grep { ... } values %$hash_ref. Your question has nothing to do with Unix and is too basic to be useful (you could've gathered that and more with a simple search).
    – user313992
    Dec 17, 2019 at 10:19

1 Answer 1

3

At least two options:

  1. You have (only) the data structure you visioned in your question. Then you will have to iterate through the whole "list" every time you want to find a match. You don't have to write a loop though, you can use the map function:
use strict; use warnings;
my %entries = (
    '0y7vfr1234' => {
        'username' => '[email protected]',
        'password' => 'some-random-password123',
        'uri' => 'ww1.example.com',
        'index' => 14
    },
    'v2rbz1568' => {
        'username' => '[email protected]',
        'password' => 'some-random-password125',
        'uri' => 'ww3.example.com',
        'index' => 29
    }
);
my $uri = <STDIN>;
chomp $uri;
if (grep { $uri eq $_ } map { $_->{'uri'} } values %entries) {
   print "URI $uri is in the list\n";
} else {
   print "URI $uri is NOT in the list\n";
}
  1. You manage a separate index into the hash so that you can do quick lookups. An index means you have a separate hash mapping URIs to the items of the actual hash:
use strict; use warnings;
my %entries = (
    '0y7vfr1234' => {
        'username' => '[email protected]',
        'password' => 'some-random-password123',
        'uri' => 'ww1.example.com',
        'index' => 14
    },
    'v2rbz1568' => {
        'username' => '[email protected]',
        'password' => 'some-random-password125',
        'uri' => 'ww3.example.com',
        'index' => 29
    }
);
my %index = map { $entries{"uri"} => $_ } keys %entries;

my $uri = <STDIN>;
chomp $uri;
my $item = $index{$uri};
if (defined($item)) {
   print "URI $uri is in the list\n";
} else {
   print "URI $uri is NOT in the list\n";
}

This is also convenient in that you get the $item directly as a result of the lookup, if you want to access the already-existing entry in the hash in addition to just checking whether it exists, e.g. print "Index: ",$entries{$item}->{'index'},"\n";

In this second case you'll have to update the index manually every time you add/update/delete URIs in the "list":

$entries{"v84x9v8b9"} = { uri => "ww49", ... };
$index{"ww49"} = "v84x9v8b9";

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .