1

I have two arrays that are associated. The first has what would be a "key" in a hash, the second has the "value". There are multiple instances of each "key" in the array, and the value associated with each key can be either yes, or no. A quick example:

@1 = ('NET1020, NET0190, NET1020, NET0230,
       NET1020, NET1639, NET0820, NET1639');

@2 = ('yes, yes, no, no,
       yes, no, yes, no');

Notice that there are both yes and no values associated with the "key" NET1020.

I need to use @1 to 1st look for duplicates and remove them from both arrays, and if one of the values is no in @2, then that needs to be the value for the "key" in @1. If not, then the value can be yes. Basically what I need to end up with is:

%1-2 = (
    "NET1020"  => "No", 
    "NET0190"  => "Yes",
    "NET0230"  => "No",
    "NET1639"  => "No",
    "NET0820"  => "Yes",
);

I hope I have been clear enough in my explanation. I am a perl novice and am at a loss as to where to even start.

Thanks for you help.

ohm
  • 73
  • 4
  • Are you a novice trying to learn something or a novice trying to get this job done quickly? – Gene Mar 04 '14 at 15:32
  • Both. I am already getting this data by parsing an XML, and a csv, now I need to bounce them off each other. – ohm Mar 04 '14 at 15:40
  • It would be easier if you built a hash from the get-go instead of trying to merge two arrays. Is there a reason you don't do that? – ThisSuitIsBlackNot Mar 04 '14 at 16:03
  • Your question is misleading. You're not simply "removing duplicates", which would mean that you would throw away *subsequent* entries. You're performing another compression entirely. You simply want the value "no" if it is associated with a key in your solution set. – Axeman Mar 04 '14 at 16:34
  • "It would be easier if you built a hash from the get-go instead of trying to merge two arrays. Is there a reason you don't do that?" I suppose not. The information is coming from two columns in a CVS file. – ohm Mar 04 '14 at 18:26
  • @ohm Then build a hash as you parse the CSV. You are making your application more complex and harder to maintain with this approach. – ThisSuitIsBlackNot Mar 04 '14 at 22:30

5 Answers5

2
my @names = split /\s*,\s*/, 'NET1020, NET0190, NET1020, NET0230, NET1020, NET1639, NET0820, NET1639';
my @flags = map { $_ eq 'yes' }
            split /\s*,\s*/, 'yes, yes, no, no, yes, no, yes, no';

my %flags;
for (0..$#names) {
   if (exists($flags{ $names[$_] })) {
      $flags{ $names[$_] } &&= $flags[$_];
   } else {
      $flags{ $names[$_] } = $flags[$_];
   }
}

print($_, ": ", $flags{$_} ? "Yes" : "No", "\n")
   for sort keys %flags;

You shouldn't work with yes and no or Yes and No. This solution converts yes and no to a more appropriate true and false on input. If you want to output Yes and No for true and false, you'd handle that on output, as shown.

You can even see the benefit of using true and false in this little job. It works by ANDing the flags of the entries with the same name. (False wins out when ANDing. True wins out when ORing.)

Zaid
  • 36,680
  • 16
  • 86
  • 155
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • @Borodin at least there arent both cuddled *and* non-cuddled *shudder* – Hunter McMillen Mar 04 '14 at 17:09
  • Added better explanation. – ikegami Mar 04 '14 at 17:19
  • I actually used yes and no for ease writing the description. I am getting the values from parsing a CSV file and the actual values are Compliant/Non-Compliant. I suppose I could do a quick replace for(@A1){s/Compliant/false/g} and then change them back, but this seems unnecessary. – ohm Mar 04 '14 at 17:54
  • 1) You didn't use `yes` and `no`. You asked to provide different values as outputs (`Yes` and `No`) than you had for inputs (`yes` and `no`). 2) You'd replace `$_ eq 'yes'` with `$_ eq 'Compliant'`. `s/.../false/` does not create a false value. 3) If this is your entire program, maybe. Though `&&` is much less error-prone than `$data{$key} = 'no' if $a2[$i] eq 'no';`. For example, it doesn't convert the case like you requested. – ikegami Mar 04 '14 at 18:03
0

Is the order of the array content important?

If not, i would suggest to use a hash:

    my %hash = ();
    for my $i (0 .. $#array1)
    {
          $hash{ $array1[$i] } =  $array2[$i]; 
    }
@filtered1 = keys %hash;
@filtered2 = values %hash;

Note: code untested and not validated

Martin Baumgartner
  • 3,524
  • 3
  • 20
  • 31
  • It depends on what you mean by that. As long as the associations don't change, then no. ie @1[0] - @2[0] | @1[1] - @2[1] |@1[2] - @2[2] etc. – ohm Mar 04 '14 at 15:42
  • the assosication does not change, you cant control how perl handles the hash internally, at least as far as i know. For a better understanding, if you use this code the order could be: @1[1] - @2[1] | @1[0] - @2[0] |@1[2] - @2[2] – Martin Baumgartner Mar 04 '14 at 15:44
  • If you look closely the array only has a single element, so this code doesn't do what you would expect. – Hunter McMillen Mar 04 '14 at 15:49
  • I tried this and though it does dedupe the array, it does not take into account the yes/no factor. The original array had 6 "no"s and after running this code, the array only had 4 "No"s. No should take presidence. Everything should be set to yes, unless a No is present. – ohm Mar 04 '14 at 15:55
  • This is just wrong. It won't even compile with `use strict` in place (and without it why declare `%hash` and `$i`?) and it makes no attempt to check whether *any* of the values in `@array2` for a given key is `no`. – Borodin Mar 04 '14 at 16:33
0

Just loop through the values and add to/update your hash as needed.

1.If the key already exists, only update it if the existing value is not 'no'

2 If the key doesn't exist, add it to the hash

#!/usr/bin/perl
use strict;
use warnings;

use Data::Dumper;

my @arr1 = 
   qw(NET1020 NET0190 NET1020 NET0230 NET1020 NET1639 NET0820 NET1639);
my @arr2 = 
   qw(yes yes no no yes no yes no);

my %h;
KEY:
foreach my $i ( 0 .. $#arr1 ) {
   # get the values out of your parallel arrays
   my $key = $arr1[$i];
   my $val = $arr2[$i];

   # if the key is already in your hash and does not have this value
   if (exists $h{$key} && $h{$key} ne $val) {
      # don't change the key if the value is currently 'no'
      next KEY if lc $h{$key} eq 'no';

      # update if the value was not yes, meaning this is going from yes -> no
      $h{$key} = $val;
   }

   # if the key didn't exist already add it
   $h{$key} = $val;
}
print Dumper \%h;

__END__   
{ 'NET0190' => 'yes',
  'NET1639' => 'no',
  'NET0230' => 'no',
  'NET0820' => 'yes',
  'NET1020' => 'no'
};

If there were no duplicates you could do this in a single line with a hash-slice:

my %h; 
@h{@arr1} = @arr2; 
Hunter McMillen
  • 59,865
  • 24
  • 119
  • 170
  • Hunter, your perl Kung Fu is tremendous. This works! I am reverse engineering this right now so that I fully understand what you are doing here. I don't just want an answer, I want to know how to do this. Thanks a million for both the solution and knowledge!! – ohm Mar 04 '14 at 17:08
  • Everything works to a point. I have two keys that have multiple "yes" values, and one "No" value. They are returning as "yes" when they should say "No". – ohm Mar 04 '14 at 17:20
0

I had to modify your list to work better as Perl. You gave a multi-line string and not an array. Basically, my approach is pretty standard CPAN. List::Util and List::MoreUtils.

zip creates a order-based correspondence between lists. And pairs allows you to deal with a key-value pair as ( $a, $b ).

use strict;
use warnings;
use List::Util      qw<pairmap>;
use List::MoreUtils qw<zip>;

my @_1 =  split /,\s*/ms, 
      ('NET1020, NET0190, NET1020, NET0230,
       NET1020, NET1639, NET0820, NET1639')
      ;

my @_2 = split /,\s*/ms, 
       ('yes, yes, no, no,
       yes, no, yes, no')
       ;
my %hash;
pairmap { 
    $hash{ $a } = $b unless ( $hash{ $a } // '' ) eq 'no'; 
} zip @_1, @_2
;

Of course, pairwise might be cleaner:

use List::MoreUtils qw<pairwise>;
...
pairwise { 
    $hash{ $a } = $b unless ( $hash{ $a } // '' ) eq 'no'; 
} @_1, @_2
;
Axeman
  • 29,660
  • 2
  • 47
  • 102
  • Why `@array = split /,\s*/ms, ('foo, bar');` instead of just `@array = qw(foo bar);` ? – ThisSuitIsBlackNot Mar 04 '14 at 16:24
  • @ThisSuitIsBlackNot, IMO it best illustrates the transformation needed from the poster's "code" to a usable list. – Axeman Mar 04 '14 at 16:27
  • @ThisSuitIsBlackNot, Sounds to me (and I presume Axeman) like the OP actually has the string `foo, bar`. If the value cuold be hardcoded as you suggest, he should just start with the final hash. – ikegami Mar 04 '14 at 16:29
  • @ThisSuitIsBlackNot: Like you, I think, I assumed that he had a pair of arrays that are the result of preceding code. `@1`, `@2` and the parentheses are strange notation for two strings. – Borodin Mar 04 '14 at 16:37
  • @ikegami: You have used hard-coded strings, and the same applies to that assumption. – Borodin Mar 04 '14 at 16:40
  • @Borodin, So what if we used hard-coded strings? No, nothing that was said applies. The difference is that we hardcoded the OP's input (and thus easily replaceable), where as TSIBN suggested we hardcoded something different. In other words, we used the OP's input unchanged. We didn't write code that happens to the produce the same output from a different input. If you're going to do that, the answer would be `my %flags = ( "NET1020" => "No", "NET0190" => "Yes", "NET0230" => "No", "NET1639" => "No", "NET0820" => "Yes" );` – ikegami Mar 04 '14 at 18:09
0

It isn't clear whether you are starting with Perl arrays or simple comma-separated strings. I am also unsure whether you want the resultant hash values capitalised as you show, or the same as the input values.

This short program will do what you need. It simply assigns hash elements using each key from @a1 and an initial value of yes. Thereafter, if a corresponding no is encountered in @a2 then the element's value is set to no.

use strict;
use warnings;

my @a1 = qw{  NET1020 NET0190 NET1020 NET0230 NET1020 NET1639 NET0820 NET1639  };
my @a2 = qw{  yes     yes     no      no      yes     no      yes     no       };

my %data;
for my $i (0 .. $#a1) {
  my $key = $a1[$i];
  $data{$key} = 'yes' unless $data{$key};
  $data{$key} = 'no' if $a2[$i] eq 'no';
}

use Data::Dump;
dd \%data;

output

{
  NET0190 => "yes",
  NET0230 => "no",
  NET0820 => "yes",
  NET1020 => "no",
  NET1639 => "no",
}
Borodin
  • 126,100
  • 9
  • 70
  • 144