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

I have two sets of data one in a database, the other by user input eg
my @db = (1, 3, 5, 7, 9, 11); my @in = (1, 2, 5, 8, 9, 10, 13);
depending on the results of the comparison I want one of two things to happen. I have worked out the expressions for each event but I have not been able to turn the two into a single conditional. I have been getting confused by the fact that the first expression has
@lookup{@in} = ();
and the other is reversed:
@lookup1{@db} = ();
The two expressions are as follows:
my %lookup; @lookup{@in} = (); foreach my $elem (@db) { if (exists $lookup{$elem}) { @db = grep {not exists $lookup{$_}} @db; } }
and:
@lookup{@db} = (); foreach my $elem (@in) { if (not exists $lookup{$elem}) { push(@db, $elem); } }
If someone could kindly help me to do an if...elsif or whatever else to trigger one if the other is not true I would be very grateful.

Thanks

Replies are listed 'Best First'.
Re: Help needed to make a conditional statement
by djantzen (Priest) on Dec 04, 2002 at 03:42 UTC

    First, look at what you're doing here:

    my %lookup; @lookup{@in} = (); # For every $elem in @db ... foreach my $elem (@db) { # see if it exists in the lookup hash ... if (exists $lookup{$elem}) { # and if it does then search @db (*again*) looking for # items that do not appear in the lookup hash. You're # also modifying the array you're iterating over (@db), # which can yield unpredictable results, so don't do it ;) @db = grep {not exists $lookup{$_}} @db; } }

    In the second case, you could utilize a grep very similar to the one above, and simplify the if/push syntax.

    How about this as a solution:

    my (%db_lookup, %in_lookup); @db_lookup{@db} = (); @in_lookup{@in} = (); if ($condition1) { # Dunno what your test conditions are, so... @db = grep { not exists $in_lookup{$_} } @db; } elsif ($condition2) { push @db, grep { not exists $db_lookup{$_} } @in; }

    Hope that helps.

      After much fiddling around I have arrived at the following:
      #!/usr/bin/perl -w #use strict; use CGI ':standard'; my @db = (1, 3, 5, 7, 9, 11); my @in = (1, 2, 5, 8, 9, 10, 13); my $var = 7; # I have this variable available to indicate the length o +f the array my (%db_lookup, %in_lookup); @db_lookup{@db} = (); @in_lookup{@in} = (); $count = 0; while ($count < $var) { if (exists $in_lookup{$count}, @db) { @db = grep { not exists $in_lookup{$_} } @db; $count += 1; } elsif (not exists $in_lookup{$count}, @db) { push @db, grep { not exists $db_lookup{$_} } @in; $count += 1; } } print "view: @db";
      That does what I need when strings match. However when strings don't match the db is unchanged from the original, wheras I need the contents of @in pushed into @db:
      my @db = (1, 3, 5, 7, 9, 11); my @in = (21, 22, 25, 28, 29, 210, 213);
      Where am I going wrong?

        A couple of problems here. First, $count will need to be specified in a more maintainable manner, such as using scalar on your arrays and finding the greater value. Second, you're looking at the value of $count in your evaluations, not the value of the index in your arrays that $count points to. Finally, by adding @db to your conditionals i.e.,if (exists $in_lookup{$count}, @db) you are ensuring that the test will return 'true' always unless the array is empty. So, the way you've got it now it executes the first conditional branch 7 times and then exits.

        I'm still struggling to see what exactly you're attempting -- if element A in array @in exists in array @db, remove A from @db. Else, if element A does not exist in array @db, append it to @db. Is this right?

        This is actually a reply to fever.

        The reference to $count is actually pure guesswork on my part. The problem is that I cannot find a way to say "if the input exists - take it out, if it doesn't - put it in". Obviously inclusion of the $count mechanism is more problematical than I'd anticipated but I can't find another approach which comes close to letting me say, "while values of @in match values of @db or the converse".

        That, I think is the crux of the problem!
Re: Help needed to make a conditional statement
by Anonymous Monk on Dec 05, 2002 at 08:17 UTC
    foreach $input (@in) { $data{$input}++ }
    foreach $db (@db) { $data{$db}++ }
    @db = [];
    foreach $value (keys %data) {if ($data{$value}) = 1 {push @db $value} }


    What does this do?


    Assuming @in = 1,3,5 and @db = 3, 5, 6
    First foreach %data = { 1=>1, 3=>1, 5=>1}
    Second foreach %data = { 1=>1, 3=>2, 5=>2, 6=>1}


    Now when the lists are combined, all keys with values
    equal to 1 only occurs once, any keys that have values
    greater than 1 occur in both lists.
      One additional note, You can also add 1 to %data for the input and 2 to %data for the db.
      Now your hash would look as followes:

      %data = { 1=>1, 3=>3, 5=>3, 6=>2}
      As you can see, now you can distinguish between keys from each array as
      well as the union between the two.

      needles
        Thanks for your input. I have actually gone with fever's grep solution but I will give this a try to see if I can get my head round it!!