Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Need to Create an Array of Differences

by scottj (Monk)
on Aug 02, 2004 at 23:21 UTC ( #379470=perlquestion: print w/replies, xml ) Need Help??

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

I have an array @a that contains some data. @b contains a subset of @a. I'm trying to find a concise way to create @c, which will essentially contain everything in @a that is not in @b. I realize that I can do something like this:
for my $a (@a) { unless (grep(/^$a$/,@b)) { push(@c,$a); } }
This method is crude and slow, but it does seem to work. I have done many searches on this site on arrays and sorting, and I get the feeling that there is a method for achieving my desired results in a faster, more concise manner. Can anybody help me?

Thanks,
Scott

Update: merlyn and Fletch kindly pointed out that this is in the FAQ. Zaxo offered a solution that worked extremely well for me (since I had already eliminated dupes in my arrays). Thanks for the help once again, monks! :)

Replies are listed 'Best First'.
Re: Need to Create an Array of Differences
by Aristotle (Chancellor) on Aug 02, 2004 at 23:28 UTC

    If they're merely strings, you can do this easily by using a lookup hash. (If not, you should still use a hash, you just need to mangle things to get unambiguous keys.)

    my %in_b; undef @in_b{ @b }; my @c = grep !exists $in_b{ $_ }, @a;

    Note that in this form it may conflate values from @b you wanted to treat as separate (empty string vs undef f.ex).

    Makeshifts last the longest.

Re: Need to Create an Array of Differences
by Zaxo (Archbishop) on Aug 02, 2004 at 23:32 UTC

    Here's the hash way,

    # given @a, @b my @c = do { my %a; @a{@a} = (); # slice of %a, values undef delete @a{@b}; # delete the @b slice keys %a; };
    That may not be exactly what you want. If @a contains duplicate elements you wish to keep, this will not duplicate them in @c. If the order of @a is important, this will not preserve it in @c.

    In those cases I would write code much like yours or Aristotle's or revdiablo's.

    After Compline,
    Zaxo

      I ended up doing something like this:
      # given @a, @b my %a; @a{@a} = (); delete @a{@b}; my @c = keys %a;
      This code works just as well as my previous code with the exception that the new code is much faster. Thanks!

      -Scott

        I assume the code does what you need, but there are a few caveats that should be mentioned. This code will change the order of the values, and it will eliminate any duplicates. This may or may not be a problem, depending on what it's being used for.

        Update: these caveats were noted, in Zaxo's original node, I just failed to notice. My apologies.

Re: Need to Create an Array of Differences
by revdiablo (Prior) on Aug 02, 2004 at 23:29 UTC

    Any time you want to know if one value is in a set of others, you probably want a hash. Convert @b to a hash (with the values as keys), then loop on @a, checking if the values exist in %b. Here's an example:

    my @a = qw(one two three four); my @b = qw(two four); my %b; @b{ @b } = (1)x@b; # one of many ways to create the hash for (@a) { unless ($b{$_}) { print "$_ is not in \@b\n" } }
Re: Need to Create an Array of Differences
by tachyon (Chancellor) on Aug 03, 2004 at 02:09 UTC

    Hashes are fine in many cases. Algorithm::HowSimilar is an alternative approach.

    use Algorithm::HowSimilar 'compare'; my ( $av_similarity, $sim_ary1_to_ary2, $sim_ary2_to_ary1, $ref_ary_matches, $ref_ary_in_ary1_but_not_ary2, $ref_ary_in_ary2_but_not_ary1 ) = compare( [ 1,2,3,4 ], [ 3,4,5,6,7 ] );

    cheers

    tachyon

Re: Need to Create an Array of Differences
by Fletch (Bishop) on Aug 03, 2004 at 00:12 UTC

    See also perldoc -q difference.

Re: Need to Create an Array of Differences
by Anonymous Monk on Aug 03, 2004 at 04:18 UTC
    For readibility & ease-of-use I like Set::Array. It has a wealth of set operation, including the one you're looking for:
    use Set::Array; ... my $set_a = Set::Array->new(@a); my $set_b = Set::Array->new(@b); my @c = $set_a->difference(@set_b);
Re: Need to Create an Array of Differences
by xtype (Deacon) on Aug 03, 2004 at 18:30 UTC
    Here is another take on the lookup hash, which I use now and then.
    Much the same as Aristotle's bit. Although, I thought that I would
    throw it in anyway, since I had it ready before reading the replies.
    ## use hash search to find everyone in @a but not in @b. my %temphash = map { $_ => 1 } @b; my @c = grep { !$temphash{ $_ } } @a;

    -xtype
      ther's a better way to use grep...
      
      my @difference =  grep /^@a1/, @a2;
      
      #example of sub...
      
      my $difference = diff(\@a1,\@a2);
      
      sub diff {
          my @diff =  grep /[^@{$_[0]}]/, @{$_1};
          return \@diff;
      }
      
      Marco (nemux)
      Bye.
      
        sorry ... is impossible to see in my previous message the -> [] 
        
        then with square brackets visible is :
        
        sub diff_2 {
            my @diff =  grep /[[^@{$_[0]]}]/, @{$_[1]};
            return \@diff;
        }
        
        Marco
        
        
Re: Need to Create an Array of Differences
by metaperl (Curate) on Dec 16, 2004 at 19:07 UTC

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (2)
As of 2022-05-17 11:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you prefer to work remotely?



    Results (65 votes). Check out past polls.

    Notices?