Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

compare arrays, and list the difference

by iza (Monk)
on Oct 03, 2012 at 10:29 UTC ( [id://997024]=perlquestion: print w/replies, xml ) Need Help??

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

hello monks :)

it's waaay too long i haven't been coding (at all), and especially coding in perl, so please forgive me for this dumb dumb dumb question. I haven't found it answered, so either I can't even properly search or it's so dumb nobody ever dared to ask.

my need : I've got two arrays, say :
my @source=(1, 2, 3, 4, 5, 7); my @target=(0, 1, 3, 4, 6);
and what I want is :
@values_in_target_not_in_source would be (6, 0) @values_in_source_not_in_target would be (2, 5, 7)

of course I can see plenty of solutions requiring to go throught the lists multiple times

I wish there was some kind of "elegant and yet efficient" (ok : perlish) solution

any idea ?

this is quite close but then I'd need to go throught one of the lists one more time :-/

Replies are listed 'Best First'.
Re: compare arrays, and list the difference
by choroba (Cardinal) on Oct 03, 2012 at 10:48 UTC
    my %s; undef @s{@source}; my %t; undef @t{@target}; my %tns = %t; delete @tns{@source}; my %snt = %s; delete @snt{@target}; my @tns = keys %tns; my @snt = keys %snt;
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: compare arrays, and list the difference
by Lotus1 (Vicar) on Oct 03, 2012 at 13:12 UTC

    List::Compare does exactly what you need and it is easy to use.

    @Llist = qw(abel abel baker camera delta edward fargo golfer); @Rlist = qw(baker camera delta delta edward fargo golfer hilton); $lc = List::Compare->new(\@Llist, \@Rlist); get_unique() Get those items which appear (at least once) only in the first list. @Lonly = $lc->get_unique; @Lonly = $lc->get_Lonly; # alias get_complement() Get those items which appear (at least once) only in the second list. @Ronly = $lc->get_complement; @Ronly = $lc->get_Ronly; # alias
Re: compare arrays, and list the difference
by BrowserUk (Patriarch) on Oct 03, 2012 at 11:03 UTC

    For smallish integers only:

    #! perl -slw use strict; my @source=(1, 2, 3, 4, 5, 7); my @target=(0, 1, 3, 4, 6); my( $src, $tgt ) = ( '', '' ); vec( $src, $_, 1 ) = 1 for @source; vec( $tgt, $_, 1 ) = 1 for @target; my @srcNotTgt = grep !vec( $tgt, $_, 1 ), @source; my @tgtNotSrc = grep !vec( $src, $_, 1 ), @target; print "srcNotTgt:@srcNotTgt"; print "tgtNotSrc:@tgtNotSrc"; __END__ C:\test>997024 srcNotTgt:2 5 7 tgtNotSrc:0 6

    The same logic works for arbitrary data using hashes instead of bitstrings.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    RIP Neil Armstrong

Re: compare arrays, and list the difference
by roboticus (Chancellor) on Oct 03, 2012 at 11:08 UTC

    lza:

    You can't have looked very hard for the answer, as it's a commonly-asked question. Read perldoc perlfaq4.

    Update: I was probably expecting a bit much in thinking that beginning programmers would be able to adapt the code in perlfaq4 "How do I compute the difference of two arrays? How do I compute the intersection of two arrays". As pennance, here's a simple adaptation:

    #!/usr/bin/perl use 5.14.0; use warnings; use autodie; my @source=(1, 2, 3, 4, 5, 7); my @target=(0, 1, 3, 4, 6); my %count = (); $count{$_} = 1 for @source; $count{$_} |= 2 for @target; my (@not_in_src, @not_in_tgt, @in_both); for (sort keys %count) { push @not_in_tgt, $_ if $count{$_} == 1; push @not_in_src, $_ if $count{$_} == 2; push @in_both, $_ if $count{$_} == 3; } print "Not in source: ", join(", ", @not_in_src), "\n"; print "Not in target: ", join(", ", @not_in_tgt), "\n"; print "In both: ", join(", ", @in_both), "\n";

    ...roboticus

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

      Which of FAQ4 answers the OPs question?


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      RIP Neil Armstrong

        BrowserUk:

        It seems that "How do I compute the difference of two arrays? How do I compute the intersection of two arrays?" gives the essential information. Perhaps my expectation that it's a simple step from the FAQ to this question is a bit much?

        In any case, since it's not an exact match to the question, I shouldn't have said that it's a FAQ.

        ...roboticus

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

      it's the faq I was referring to in my question

      I didn't know the $count{$_} |= 2 for @target; trick - I like it, thanks !

Re: compare arrays, and list the difference
by daxim (Curate) on Oct 03, 2012 at 10:42 UTC
    These are set operations, see Set::Scalar or similar module.
Re: compare arrays, and list the difference
by AnomalousMonk (Archbishop) on Oct 03, 2012 at 14:09 UTC

    Yet another approach, not necessarily better:

    >perl -wMstrict -le "use List::MoreUtils qw(part); use Data::Dump qw(pp); ;; my @source = (1, 2, 3, 4, 5, 7); my @target = (0, 1, 3, 4, 6); ;; my %diff; ++$diff{$_} for @source; --$diff{$_} for @target; ;; my ($ar_in_t_not_s, undef, $ar_in_s_not_t) = part { 1 + $diff{$_} } keys %diff; ;; print 'in t not s: ', pp $ar_in_t_not_s; print 'in s not t: ', pp $ar_in_s_not_t; " in t not s: [6, 0] in s not t: [7, 2, 5]
Re: compare arrays, and list the difference
by Anonymous Monk on Oct 03, 2012 at 10:51 UTC

    this is quite close but then I'd need to go throught one of the lists one more time :-/

    I think you have to :)

    #!/usr/bin/perl -- use strict; use warnings; use Data::Dump; my @source=(1, 2, 3, 4, 5, 7); my @target=(0, 1, 3, 4, 6); my (@union, @intersection, @difference); my %count = (); foreach my $element (@source, @target) { $count{$element}++ } foreach my $element (keys %count) { push @union, $element; push @{ $count{$element} > 1 ? \@intersection : \@difference }, $e +lement; } dd \@source, \@target; dd \@union; dd \@intersection; dd \@difference; my @values_in_target_not_in_source = do { my %fuf; @fuf{ @union } = undef; delete @fuf{ @target }; keys %fuf; }; my @values_in_source_not_in_target = do { my %fuf; @fuf{ @union } = undef; delete @fuf{ @source }; keys %fuf; }; dd \@values_in_target_not_in_source , \@values_in_source_not_in_target + ; __END__ ([1 .. 5, 7], [0, 1, 3, 4, 6]) [6, 4, 1, 0, 3, 7, 2, 5] [4, 1, 3] [6, 0, 7, 2, 5] ([7, 2, 5], [6, 0])
Re: compare arrays, and list the difference
by Anonymous Monk on Oct 03, 2012 at 12:53 UTC
    If you can afford to spend the memory, then a hash is a great way to find duplicates. Put the keys in the hash (with a dummy value) as you go and use exists() to check for the presence of the key. If the key exists, the value has been seen before. Be careful not to test for truth; zero is a key that exists, but it is false.
Re: compare arrays, and list the difference
by Anonymous Monk on Oct 03, 2012 at 16:37 UTC
    Yup... no matter what you want to do, there's already three modules out there in CPAN that do it. :-}
      definitely - as usual, in perl, tmtowtdi :-)

Log In?
Username:
Password:

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

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

    No recent polls found