LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   How to compare two lists (arrays) in perl (https://www.linuxquestions.org/questions/programming-9/how-to-compare-two-lists-arrays-in-perl-636686/)

WindowBreaker 04-20-2008 08:24 PM

How to compare two lists (arrays) in perl
 
I've got two lists (1 & 2). All I need to do is remove elements from list 1 if that element is listed in list 2. Here's how I'm doing it now, but I feel it's the hard way.

Code:

for(my $i=0; $i<=$#list1; $i++){
  foreach my $element (@excludes){
    if ($list1[$i] =~ m/$element/i){
      delete $links[$i];
    }
  }
}


I know I'll feel dumb once I see how to do it better.

Thanks

PS: I'm consciously using 'delete' instead of 'splice' because I need the elements in list1 to maintain their positions.

osor 04-20-2008 10:13 PM

If you want to use the same criteria as above (case-insensitive, fixed-string matches), then the method above is essentially how I would go about doing it (although I might write it with two foreach-style loops).

If, however, you want to test for exact equality of strings (not matches, and not case-insensitive), you can use the uniqueness property of hashes. E.g.,
Code:

#!/usr/bin/perl -w

use strict;

my @list1 = qw(this is a list of strings in list1);
my @list2 = qw(this is the same for the second list);

# I want @list1 to end up as: (undef, undef, "a", undef, "of", etc.)

my %seen;
$seen{$_}++ for @list2;
$seen{$_} && undef $_ for @list1;


bigearsbilly 04-21-2008 02:16 AM

work that one out!

Code:


#!/usr/bin/perl

@A = qw (tom dick harry);
@B = qw (peter paul dick harry);

@C = keys %{ { map { $_, 1} (@A, @B) } };
print "@C\n";

Code:

[billy@daffy lxq]$ ./1.pl
tom dick peter harry paul

doubtfully more efficcient, less typing,
unreadable!

osor 04-21-2008 01:00 PM

Quote:

Originally Posted by bigearsbilly (Post 3127628)
doubtfully more efficcient, less typing,
unreadable!

And unfortunately not what the OP wants :). The code you wrote provides the mathematical operation of “set union” whereas the OP asked for “set difference”. So, to use your code style, you would want:
Code:

@C=grep!${{map{$_,1}@B}}{$_},@A;
Additionally, the OP wants the original indexing preserved, so if you originally have @A=qw(dick tom harry), the OP wants $C[1] to be "tom" (rather than $C[0]). So in that case,
Code:

@C=map{!${{map{$_,1}@B}}{$_}&&$_||undef}@A;
If you want to modify the original array, you could do:
Code:

${{map{$_,1}@B}}{$_}&&undef$_ for@A;
I should point out that the above is less efficient than my original since the same anonymous hash of @B is being created in each iteration (unless of course it is optimized out).

osor 04-21-2008 01:26 PM

See also List::Compare, which does set unions and complements (where set complement is the same thing as set difference). Unfortunately, it does not preserve indices.

WindowBreaker 04-22-2008 10:06 AM

Thanks for all the answers. The map command you gave me (below) works awesome. Now if I could only understand it.
Code:

@C=map{!${{map{$_,1}@B}}{$_}&&$_||undef}@A;
Help me understand how this works. I understand this part.
Code:

map{$_,1}@B
But what's the rest doing with it?

I understand the following part returns the element from @A if true, else returns undef.
Code:

&&$_||undef
Here's the real part I don't get.
Code:

!${{map{$_,1}@B}}{$_}
It looks as if $_, the current element from @A is used as the key in a hash. I just don't see how the ${{}} enclosing the output of the embedded map has anything to do with a hash.

Maybe I'll get it if I keep working with it, but a little help would be nice.

Telemachos 04-22-2008 11:22 AM

Apologies all: Post removed since I realized I don't see it at all. Anyone care to enlighten us?

osor 04-22-2008 01:53 PM

Quote:

Originally Posted by WindowBreaker (Post 3129178)
The map command you gave me (below) works awesome.

I would recommend against using it in production code ;).
Quote:

Originally Posted by WindowBreaker (Post 3129178)
Here's the real part I don't get.
Code:

!${{map{$_,1}@B}}{$_}
It looks as if $_, the current element from @A is used as the key in a hash. I just don't see how the ${{}} enclosing the output of the embedded map has anything to do with a hash.

Well, when you initialize a hash to a list, every even-indexed element becomes a key and every odd-indexed element becomes a value corresponding to the key preceding it. So, if you have:
Code:

%hash = (1, 2, 3, 4);
You’ll note that $hash{1}==2 and $hash{3}==4. Likewise, the following statements produce equivalent (not equal, but semantically equivalent) hashes:
Code:

%hash1 = (map{$_,1} @B);
%hash2 = ();
$hash2{$_} = 1 for @B;

When you use curly braces ({}) around a list of items, it creates an anonymous hash with such an initialization and returns a reference to it. So the following hash references are equivalent:
Code:

%hash = (map{$_,1} @B);
$hash_ref1 = \%hash;
$hash_ref2 = { map{$_,1} @B };

If you use a sigil ($, @, or %) prior to a set of curly braces, the inside is assumed to be a reference, and the result is a dereferencing in the appropriate manner. So the following three hashes are equivalent:
Code:

%hash1 = (map{$_,1} @B);
$hash_ref = { map{$_,1} @B };
%hash2 = %{ $hash_ref };
%hash3 = %{ { map{$_,1} @B } }

If you put all these concepts together, the boolean scalars at the end are the same:
Code:

%seen = ();
$seen{$_}=1 for @B;
$harry_seen1 = $seen{"harry"};
$harry_seen2 = ${{map{$_,1}@B}}{"harry"};

Unfortunately, if you put the anonymous hash dereference inside a loop, it is created and destroyed on each iteration (which is quite a bit more work than creating a separate hash once).

bigearsbilly 04-23-2008 05:31 AM

Quote:

And unfortunately not what the OP wants . The code you wrote provides the mathematical operation of “set union” whereas the OP asked for “set difference”.
oh well! need a better spec.
Quote:

Additionally, the OP wants the original indexing preserved
he didn't say that!

anyway, i think we can agree that this is an exercise in unreadable code but interesting
in a programming exercise context.

I think the OP's original is much better as regards keeping it simple.

bigearsbilly 04-23-2008 05:34 AM

well windowbreaker,

if you find this style interesting intellectually, you should
have a go at LISP or maybe Haskell.

they are especially good at this sort of lateral thinking.

syg00 04-23-2008 06:44 AM

LISP ????... LISP ????. Shouldn't that be reducable to one line in lisp ???.

Haven't seen (as in used) that since (early) Uni days. And that was a while ago - images of PDP's wander past ...
Write a syntax parser in lisp ... foggy/drunken undergrad days evoked.
Billy, don't do that to a fella.

makyo 04-23-2008 06:46 AM

Hi.

If one uses perl a lot, the item below may be useful. Chapter 4 deals with arrays, and recipes are provided for simple and symmetric differences of arrays. The first edition can be obtained used for less than $10 ... cheers, makyo

Code:

Title: Perl Cookbook
Subtitle: Solutions and Examples for Perl Programmers
Author: Tom Christiansen, Nathan Torkington
Edition: 1st
Date: August 1998
Publisher: O'Reilly
ISBN: 1565922433
Pages: 794
Categories: perl, scripting, programming, algorithms, recipes, code
Comments: Note that a 2nd edition is available, 2003
Comments: 4.5 stars (107 reviews, 2008.04) at Amazon for 2nd
Comments: Examples tar/zip file available, see O'Reilly page.


osor 04-23-2008 09:22 PM

Quote:

Originally Posted by bigearsbilly (Post 3130076)
he didn't say that!

How else do you interpret this?
Quote:

Originally Posted by WindowBreaker (Post 3127461)
I need the elements in list1 to maintain their positions.


bigearsbilly 04-24-2008 03:01 AM

doh


we all have off days.


All times are GMT -5. The time now is 10:47 PM.