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

I am fairly new to perl, so please be patient. I am trying to compare a complex hash to an array of hashes. I have been successful, however my way I believe is slow. I have tried to do it another way, but I receive a different answer. I believe it has something to do with the slice operator and $index. The following is the ugly but successful way
my $matched=0; foreach my $id (keys %manager){ foreach my $manager (keys %{$manager{$id}}){ for my $i (0..$#{$manager{$id}{$manager}{'members'}}){ my $index=0; foreach my $emp (@found){ if ($emp->{'ID'}=~/$manager{$id}{$manager}{'members'}[ +$i]/i){ splice(@found, $index, 1); print $emp->{'Name'} . "\n"; $matched++;} $index++;}}}}
Here is the other, but unsuccessful approach:
my $matched=0; my $index=0; foreach my $emp (@found){ DN: foreach my $id (keys %manager){ foreach my $mgr (keys %{$manager{$id}}){ for my $i(0..$#{$manager{$id}{$mgr}{'members'}}){ if ($emp->{'ID'}=~/$manager{$id}{$mgr}{'members'}[$i]/ +i){ print $emp->{'Name'} . "\n"; $matched++; splice(@found, $index, 1); --$index; last DN;}}}} $index++;}
I also have another question. I have a hash that looks something like
%hash= ( $email => { name => 'joe' id = > '123' })
I than push (@array, $hash{$email}); Is it than possible to get the value $email in the hash from the array?
foreach my $email (@array){ print $email->{'name'}; #ok print $email;} #error hash refs
Thanks for any help

Replies are listed 'Best First'.
Re: splice question (change your data structure)
by Ovid (Cardinal) on Feb 26, 2003 at 17:26 UTC

    There are a variety of ways of looking at this problem, however, in playing with it, I stumbled because I wouldn't figure out your intent. Looking at your code snippets, I see that each of them is four nested loops! This absolutely won't scale. You state that you believe your way is slow, but I would suggest that you prove your way is slow before continuing. If you have a small data set, it might still be okay.

    Further, I still don't know exactly what you're trying to accomplish. I wrote the following test program which, from what I can tell, mimics what you're trying to do, but it still doesn't tell me why you're trying to do it:

    use strict; use Data::Dumper; my @found = map { {ID=>$_} } qw( abC e8q xxx 11m ); my $matched = 0; my %manager = ( 1 => { bob => { members => [qw{ abc def gdF }] } }, 7 => { alice => { members => [qw{ 5hg 8DF 9iw }] }, charlie => { members => [qw{ 2je e8q 11M }] } } ); foreach my $id (keys %manager){ foreach my $manager (keys %{$manager{$id}}){ for my $i (0..$#{$manager{$id}{$manager}{'members'}}){ my $index=0; foreach my $emp (@found){ if ($emp->{'ID'}=~/$manager{$id}{$manager}{'members'}[$ +i]/i){ splice(@found, $index, 1); print $emp->{'Name'} . "\n"; $matched++;} $index++;}}}} print "Matched: ($matched) ", Dumper \@found;

    Give that the first data structure is so complex, I would suggest that your task it to rethink the construction of said structure or to construct another structure that accomplishes the needed task. For example, we see that each manager (Bob, Alice, and Charlie) has an array of members. In you regular expression, you attempt to match this in a case-insensitive manner, but your match will match if an ID is a substring of a member. If you want to make an exact, but case insentive match, perhaps while constructing the complex data structure, you can make those "members" the keys in a hash (while lower-casing them). This allows the following:

    @found = grep { ! exists %members{ lc $_->{ID} } } @found;

    (This appears to match your logic where the items in the @found array are those items that were not found in the data structure!)

    Thus, instead of having four nested loops, we have one pass through a data structure.

    Additionally, you could try putting this data in a database and taking advantage of the capabilities inherent their.

    In short, there are a variety of ways to deal with your problem, but if you give us a higher level description of what you need to accomplish, we may be better able to help you.

    Side note, instead of using splice to get rid of found elements, have you thought about using push to add the "not found" elements onto a different array? The logic would be easier to follow.

    if ( $emp->{ID} =~ /$some_regex/i ) { push @emp_found => $emp; print $emp->{'Name'} . "\n"; } else { push @emp_ot_found => $emp; }

    Cheers,
    Ovid

    New address of my CGI Course.
    Silence is Evil (feel free to copy and distribute widely - note copyright text)

Re: splice question
by mowgli (Friar) on Feb 26, 2003 at 17:13 UTC

    With regard to your second question - if you just take a value from a hash and push that to an array, the key this value was (is) associated with in the hash will be lost as far as the array is concerned. I.e., unless you have a field in the hash the reference to which is your value that contains said key, you won't be able to recover it.

    --
    mowgli