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.

6
  • Are you a novice trying to learn something or a novice trying to get this job done quickly? Commented Mar 4, 2014 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. Commented Mar 4, 2014 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? Commented Mar 4, 2014 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. Commented Mar 4, 2014 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. Commented Mar 4, 2014 at 18:26

5 Answers 5

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.)

Sign up to request clarification or add additional context in comments.

4 Comments

@Borodin at least there arent both cuddled and non-cuddled shudder
Added better explanation.
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.
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.
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",
}

Comments

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

5 Comments

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.
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]
If you look closely the array only has a single element, so this code doesn't do what you would expect.
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.
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.
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; 

2 Comments

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!!
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".
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
;

6 Comments

Why @array = split /,\s*/ms, ('foo, bar'); instead of just @array = qw(foo bar); ?
@ThisSuitIsBlackNot, IMO it best illustrates the transformation needed from the poster's "code" to a usable list.
@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.
@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.
@ikegami: You have used hard-coded strings, and the same applies to that assumption.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.