Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

merging two arrays with OR operation

by Anonymous Monk
on Mar 05, 2019 at 21:05 UTC ( [id://1230926]=perlquestion: print w/replies, xml ) Need Help??

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

Hello

I have two arrays that contain a binary index of 12 elements (values of each element can be only 1 or 0). I want to combine the the indexes in element to element fashion as OR operator so that I have:

@array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); #result should be @arrayResult = (0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1);

The only solution I came out with is a quite long script comparing element at position X in @array1 with the element at the same position in @array2, if both are 0 then I set the value in @arrayResult to 0 otherwise to 1. I do it in a nested loop with checking variables and so, but the whole seems to me to cumbersome for such a simple operation, and probably there is a straight operation in Perl to do this that I do not know. Or no other way the a nested loop?

Replies are listed 'Best First'.
Re: merging two arrays with OR operation
by pryrt (Abbot) on Mar 05, 2019 at 21:12 UTC

    use bitwise OR ( | ) or logical OR ( || ) on each element. It doesn't have to be a long program, if you use map or other such perl idioms

    # [id://1230926] use warnings; use strict; my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); my @arrayResult = map { $array1[$_] | $array2[$_] } 0 .. $#array1; print "arrayResult = (@arrayResult)\n"; use Test::More tests => 1; is_deeply( \@arrayResult , [0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1] )

      Much nicer than mine!! (see below, I forgot to put mine)

      use Data::Dumper; my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); my @result; if (@array1 != @array2) { $equals = 0; } else { $equals = 1; foreach (my $i = 0; $i < @array1; $i++) { if (($array1[$i] eq 0)&&($array2[$i] eq 0)) { push (@result, 0); } else{ push (@result, 1); } } } print Dumper \@result;

        There is nothing wrong with your code except that the if (($array1[$i] eq 0)&&($array2[$i] eq 0)) {... code could be replaced by:

        $result[$i] = $array1[$i] | $array2[$i];

        and your for loop is better written:

        for my $i (0 .. $#array1)

        Note that for and foreach are identical aside from the spelling so personally I never use foreach just because that way I type less.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

      I should add a "bit_cmp" to Bit::Manip that takes lists, and a bitwise op. Thoughts?

        I think having a conversion from an array of bits into your bitwise integer (and vice versa) would be a good idea.

        I'm not sure that bit_cmp(), which sounds like "bit compare" in my mental reading, quite fits with the OR or AND operations. It would make sense to me for the XOR (which is bits-not-equal) and XNOR (which is bits-are-equal, the same as ~($a ^ $b)), since those do a bitwise "compare". I guess the argument could be made that AND and OR are bitwise compares as well, but I don't think of them that way.) Since you're working with integers internally anyway, it seems easy enough just to use | & ^ ~(^) on the vars, rather than having a wrapper function to do the same thing. Then again, it wouldn't hurt...

        As you can tell, I'm ambivalent whether bit_cmp() should be added. But I definitely vote to implement a conversion to/from a bit-array.

Re: merging two arrays with OR operation
by choroba (Cardinal) on Mar 05, 2019 at 21:54 UTC
    You can also use pack and unpack to convert the array of 0s and 1s to a binary number and operate on it directly:
    my $template = 'B' . @array1; my $v1 = pack $template, join "", @array1; my $v2 = pack $template, join "", @array2; my @array_result = split //, unpack $template, $v1 | $v2;
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

      A  '(B)*' template can obviate some split-ing and join-ing (although the marginal array-length checking is no longer present):

      c:\@Work\Perl\monks>perl -le "use warnings; use strict; ;; use Test::More 'no_plan'; use Test::NoWarnings; ;; my @TESTS = ( 'degenerate case', [ [], [], [], ], 'simple cases', [ [ 0 ], [ 0 ], [ 0 ], ], [ [ 0 ], [ 1 ], [ 1 ], ], [ [ 1 ], [ 0 ], [ 1 ], ], [ [ 1 ], [ 1 ], [ 1 ], ], 'complex cases', [ [ 0, 1, 0, 1, ], [ 0, 0, 1, 1, ], [ 0, 1, 1, 1, ], ], [ [ 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, ], [ 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, ], [ 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, ], ], ); ;; VECTOR: for my $ar_vector (@TESTS) { if (not ref $ar_vector) { note $ar_vector; next VECTOR; } ;; my ($ar_x, $ar_y, $ar_expected) = @$ar_vector; ;; use constant TEMPLATE => '(B)*'; my $v1 = pack TEMPLATE, @$ar_x; my $v2 = pack TEMPLATE, @$ar_y; my @got = unpack TEMPLATE, $v1 | $v2; is_deeply \@got, $ar_expected, qq{(@got)}; } ;; done_testing; ;; exit; " # degenerate case ok 1 - () # simple cases ok 2 - (0) ok 3 - (1) ok 4 - (1) ok 5 - (1) # complex cases ok 6 - (0 1 1 1) ok 7 - (0 1 1 1 0 1 1 1 1 1 1 1) 1..7 ok 8 - no warnings 1..8
      Anonymous Monk: See also How to ask better questions using Test::More and sample data.

      Update: Oops... Originally posted with template '(a)*' (which also works, but produces longer intermediate strings) instead of '(B)*': Fixed.


      Give a man a fish:  <%-{-{-{-<

Re: merging two arrays with OR operation
by BillKSmith (Monsignor) on Mar 05, 2019 at 21:49 UTC
    I prefer to use a module where the function name documents the operation.
    # [id://1230926] use warnings; use strict; use List::MoreUtils qw(pairwise); my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); my @arrayResult = pairwise {$a or $b} @array1, @array2; print "arrayResult = (@arrayResult)\n"; use Test::More tests => 1; is_deeply( \@arrayResult , [0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1] )

    Thanks pryrt to the test script.

    Bill
Re: merging two arrays with OR operation
by roboticus (Chancellor) on Mar 05, 2019 at 21:24 UTC

    Anonymous Monk:

    Performing an operation pairwise on a pair of lists is a common enough task that there's a function in the List::MoreUtils package to help you with it: pairwise().

    So for your task, you'd simply do it like:

    $ cat pm_1230926.pl use strict; use warnings; use List::MoreUtils 'pairwise'; my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); my @arrayResult = pairwise { $a<$b ? $b : $a } @array1, @array2; print "Result: ", join(", ", @arrayResult), "\n"; $ perl pm_1230926.pl Result: 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1

    Essentially, you give it a block of code { $a<$b ? $b : $a } and a pair of lists to operate on. The values $a and $b will be the next item in the first and second list, respectively. The result will be the list of values obtained by executing the code block on each pair of items in the list. So in our code block, we check to see if $a is less than $b. If so, we'll return $b otherwise we'll return $a.

    Update: choroba suggested to me an even better way:

    my @arrayResult = pairwise { $a | $b } @array1, @array2;

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: merging two arrays with OR operation
by tybalt89 (Monsignor) on Mar 06, 2019 at 02:05 UTC
    #!/usr/bin/perl # https://perlmonks.org/?node_id=1230926 use strict; use warnings; my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); #result should be #@arrayResult = (0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @arrayResult = split ' ', "@array1" | "@array2"; use Data::Dump 'dd'; dd @arrayResult;
Re: merging two arrays with OR operation
by rsFalse (Chaplain) on Mar 05, 2019 at 22:54 UTC
    Sometimes you may compose a binary representation of number (e.g. '0b010101111111') (docs: perlnumber), eval it, use bitwise op, and return to binary representation using sprintf "%b", with '0${len}' for leading zeroes.
    #!/usr/bin/perl -l use strict; use warnings; my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); my $bits1 = eval '0b' . join '', @array1; my $bits2 = eval '0b' . join '', @array2; my $bitsResult = $bits1 | $bits2; my $len = scalar @array1; my @arrayResult = split //, sprintf "%0${len}b", $bitsResult; print "\$bits1:[$bits1], \$bits2:[$bits2], \$bitsResult:[$bitsResult]" +; print "Result: ", join(", ", @arrayResult), "\n";
    OUTPUT:
    $bits1:[1407], $bits2:[855], $bitsResult:[1919] Result: 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1
      Also you may want to look at Bitwise String Operators, and relatively new bitwise operator '|.'.
      Here I use v5.28.0:
      #!/usr/bin/perl -wl use strict; my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); my $bits1 = join '', @array1; my $bits2 = join '', @array2; print $bits1 | $bits2; use feature 'bitwise'; # from v5.22 print $bits1 | $bits2; print $bits1 |. $bits2; # So... print "Result: ", join ', ', split //, $bits1 |. $bits2;
      OUTPUT:
      011101111111 4294967295 011101111111 Result: 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1
Re: merging two arrays with OR operation
by Random_Walk (Prior) on Mar 06, 2019 at 11:10 UTC

    Here is a fun little one with map :) Just for fun, anything above is a 'better' way to do it

    use strict; use warnings; my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); print "@array1\n"; print "@array2\n"; my $i; my @result= map {$_ += $array2[$i++];int($_/2 +.6)} @array1; print "@result\n";

    Update

    WARNING Lookout though, it messes with the content of array 1
    # Here is a version that will not break array 1 use strict; use warnings; my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1); my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1); print "@array1\n"; print "@array2\n"; my $i; my @result= map {int( ($_+$array2[$i++])/2 +.6)} @array1; print "@result\n";

    Cheers,
    R.

    Pereant, qui ante nos nostra dixerunt!
      Same, but with another one variant inside the map, using shift-right op (Shift Operators):
      my @result = map { $_ + $array2[ $i++ ] + 1 >> 1 } @array1;
Re: merging two arrays with OR operation
by johngg (Canon) on Mar 06, 2019 at 23:31 UTC

    Late to the party again but here's a solution that uses vec to construct bitstrings from the arrays that can be binary ORed to produce a result. The vector and the length of the original array are held in a hashref so that problems with reconstructing the array when the vector extends to a byte boundary can be avoided.

    use 5.026; use warnings; my @array1 = ( 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1 ); my @array2 = ( 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1 ); my @arrayResult = vec2array( vecORvec( array2vec( @array1 ), array2vec( @array2 ) ) ); say join q{, }, @arrayResult; sub array2vec { my $rhVec = {}; $rhVec->{ len } = scalar @_; $rhVec->{ vec } = q{}; vec( $rhVec->{ vec }, $_, 1 ) = $_[ $_ ] for 0 .. $rhVec->{ len } - 1; return $rhVec; } sub vec2array { my @array = map { vec $_[ 0 ]->{ vec }, $_, 1 } 0 .. $_[ 0 ]->{ len } - 1; return @array; } sub vecORvec { my( $rhVec1, $rhVec2 ) = @_; my $rhNewVec; $rhNewVec->{ len } = $rhVec1->{ len }; $rhNewVec->{ vec } = $rhVec1->{ vec } | $rhVec2->{ vec }; return $rhNewVec; }

    The result.

    0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1

    I hope this is of interest.

    Update: Added some sanity checking and confined the conversion of array to vector to the rule of false/true corresponding to bit values of 0 or 1 respectively. I also added AND and XOR routines, which along with OR will cope with vectors of differing length, and borrowed and adapted AnomalousMonk's test data.

    The test results.

    Possibly a waste of time but feeping creaturism can be fun :-)

    Update 2: Took repetitive code out of the three comparison routines, (vecANDvec(), vecORvec(), vecXORvec()), and moved it into vecANYvec() which is passed a coderef telling it which operation to do. All tests still pass.

    Cheers,

    JohnGG

Re: merging two arrays with OR operation
by karlgoethebier (Abbot) on Mar 06, 2019 at 14:04 UTC

    What else should I say: This thread made my day. Very nice. Regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1230926]
Approved by Discipulus
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (2)
As of 2024-04-19 01:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found