Search code examples
arraysperlnested-loops

Need to compare one value in AoH to another value in a second AoH


Editing to be more clear:

I am going to be obtaining 2 queries from the database and storing them as AoH. The 2 queries are:

select
    ip_address, 
    testnet,
    t.email as email, 
    owner, 
    hr_manager_login as manager
from 
    lab_view 
    JOIN CMN_INT.AK_EMPLOYEE on owner = login 
    JOIN testnets t on t.name = testnet
where 
    DIVISION like 'TERM%' and TERM_DATE is not null
order by owner

and select login from CMN_INT.AK_EMPLOYEE where DIVISION = q[Terminated Employees]

For my original testing, I wasn't going to dive into the opening/using DBI, so I used dummy data. my $qr is an example of what I would get from the first query and my $qr2 is an example of what I would get from the second query. I want to see if the Manager value in the first AoH is found in the second (terminated employees) AoH. If it IS found, then the manager is a terminated employee and its name shouldn't be pushed to the %manager. I tried testing this out by inserting the below foreach code into my first foreach code, but it didn't work as the Manager Harry was still being pushed and printed.

foreach my $emp2 (@$qr2){
    if ($emp->{Manager} ne $emp2->{Login}){
        $manager{$emp->{Manager}} = 1; #capture all managers not also terminated related to $name. Done this way for when $name is undef
    }
}

I also tried to assign the $emp->{Manager} to a variable before the 2nd foreach and then use if ($M ne $emp2->{Login}), but that also didn't work.

Below is the first part of my testing code with dummy data for the queries I would run, minus all the elsifs :)

my $qr = [
    {IP=>'X.Y.Z.51',Testnet=>'bos-por-leg',Owner=>'Edmund', Email => 'bosemail', Manager => 'Tod'},
    {IP=>'X.Y.Z.52',Testnet=>'bos-por-2',Owner=>'Edmund', Email => 'boemail2',Manager => 'Tod'},
    {IP=>'X.Y.Z.53',Testnet=>'bos-por-leg',Owner=>'Edmund', Email => 'bosemail',Manager => 'Tod'},
    {IP=>'X.Y.Z.54',Testnet=>'sqa',Owner=>'Richard', Email => 'sqaemail',Manager => 'Harry'},
    {IP=>'X.Y.Z.55',Testnet=>'sqa',Owner=>'Richard', Email => 'sqaemail',Manager => 'Harry'},
    {IP=>'X.Y.Z.56',Testnet=>'fll-pro',Owner=>'Larry', Email => 'fllemail',Manager => 'Moe'},
    {IP=>'X.Y.Z.57',Testnet=>'fll-pro', Owner=>'', Email => 'fllemail',Manager => 'Tod'},
    {IP=>'X.Y.Z.58',Testnet=>'fll-pro2', Owner=>'', Email => 'flemail2', Manager => 'Curly'},
];
my $qr2 = [{Login => 'Tom'},
           {Login => 'Dick'},
           {Login => 'Harry'},
           ];
my $len = scalar @$qr;
my $l = $len;
my $a = @$qr[0]->{Owner};
func ($a);
my %ip;
my %test;
my $name;
my %manager;
my $ip_ref;
my $test_ref;
my $man_ref;
sub func{
    foreach my $emp (@$qr) {
        if ($l > 1 && $emp->{Owner} eq $a) {
            $name = $emp->{Owner} || 'Undefined'; #to use with email as $a will change as cycle thru
            $ip{$emp->{IP}} = $emp->{Testnet}; #capture all IPs related to owner $name
            $test{$emp->{Testnet}} = $emp->{Email}; #capture unique testnets only related to owner $name
            foreach my $emp2 (@$qr2){
                if ($emp->{Manager} ne $emp2->{Login}){
                $manager{$emp->{Manager}} = 1; #capture all managers not also term related to $name. Done this way for when $name is undef
                }
            }
            $l--; #to cycle thru array until reach last row
        }
    }
}
sub mail_func{
    my $n = shift;   #user 
    my $i = shift;   #ips
    my $t = shift;   #testnets
    my $m = shift;   #managers (multiple if owner is undef) --> to field
    print "User name is: $n\n";
    my @to_list;
        foreach my $value (values %{$t}){
            if ($value ne 'bosemail'){
                if (grep {$value} @to_list){next;}
                else {push(@to_list,$value . '@email.com');}
            }
        }
    foreach my $key (keys %{$m}){push(@to_list,$key . '@email.com');}
    print "@to_list\n";
    my @body;
    while ( my ( $key, $value ) = each %{$i} ) {
        my $b = "IP " . $key . " : Testnet " . $value . "\n";
        push (@body, $b);
    }
    print "@body\n";
}

In testing, I get:

User name is: Richard
[email protected] [email protected]   ##Harry shouldn't be added
IP X.Y.Z.54 : Testnet sqa
 IP X.Y.Z.55 : Testnet sqa

I appreciate all input on how to correct. If you need more code, please let me know. Also to note, the terminated employees list is pretty long, so I'm guessing I probably shouldn't even be comparing each value in one AoH to each value in another AoH via the loop method, but that is all I know :) I am trying to also see if there is a way to do what I want using only 1 query. Thank you.


Solution

  • Unless I have missed something obvious, I think you're making things too complicated.

    The following works for me.

    WARNING: It uses the 'smartmatch operator' to search for an element within an array. AFAIK this feature is still deemed 'experimental'. Others might be able to comment on the status of that feature - or suggest a more suitable alternative.

    use strict;
    use warnings;
    use feature 'say';
    
    my $qr = [
        {IP=>'X.Y.Z.51',Testnet=>'bos-por-leg',Owner=>'Edmund', Email => 'bosemail', Manager => 'Tod'},
        {IP=>'X.Y.Z.52',Testnet=>'bos-por-2',Owner=>'Edmund', Email => 'boemail2',Manager => 'Tod'},
        {IP=>'X.Y.Z.53',Testnet=>'bos-por-leg',Owner=>'Edmund', Email => 'bosemail',Manager => 'Tod'},
        {IP=>'X.Y.Z.54',Testnet=>'sqa',Owner=>'Richard', Email => 'sqaemail',Manager => 'Harry'},
        {IP=>'X.Y.Z.55',Testnet=>'sqa',Owner=>'Richard', Email => 'sqaemail',Manager => 'Harry'},
        {IP=>'X.Y.Z.56',Testnet=>'fll-pro',Owner=>'Larry', Email => 'fllemail',Manager => 'Moe'},
        {IP=>'X.Y.Z.57',Testnet=>'fll-pro', Owner=>'', Email => 'fllemail',Manager => 'Tod'},
        {IP=>'X.Y.Z.58',Testnet=>'fll-pro2', Owner=>'', Email => 'flemail2', Manager => 'Curly'},
    ];
    
    my $qr2 = [{Login => 'Tom'},
               {Login => 'Dick'},
               {Login => 'Harry'},
               ];
    
    my @dismissed = map { $_->{Login} } @$qr2;
    
    my @eligible = grep { !($_->{Manager} ~~ @dismissed) } @$qr;
    
    say $_->{Testnet}, ', ', $_->{Email} foreach @eligible;
    

    Alternative DB queries

    Since you tagged the question with Perl rather than DBI, this is parenthetical - but I think is worth noting nonetheless.

    You asked:

    I am trying to also see if there is a way to do what I want using only 1 query

    I think there is - using the NOT IN operator. Something similar to the following will probably work - although you might need to alter the syntax slightly depending on the database server you are using.

    Simple add another condition to your WHERE clause, so

    where 
        DIVISION like 'TERM%' and TERM_DATE is not null
    

    would become

    where 
        DIVISION like 'TERM%' and TERM_DATE is not null
    AND
        hr_manager_login NOT IN (select login from CMN_INT.AK_EMPLOYEE where DIVISION = q[Terminated Employees])
    

    If the number of terminated employees is extremely long (you intimate it is 'quite long' - but don't specify exactly how long) there may be performance implications for using NOT IN, but I think that is probably not the case here.