http://www.perlmonks.org?node_id=1000822


in reply to How to compare 3 lists

shammow:

For comparing multiple lists, I frequently use a hash of bit vectors, where bit 0 would be for the first list, bit 1 for the second, etc. Something like this:

$ cat lists.pl #!/usr/bin/perl use strict; use warnings; use 5.10.0; # Build a few lists my @lists; for my $i (0 .. 1+int(3*rand)) { # several lists to compare push @{$lists[$i]}, int 16 * rand for 1 .. 15; print "LIST $i: ", join(", ", sort {$a<=>$b} @{$lists[$i]}), "\n"; } # Build a scoreboard of which items are in each list my %tally; my $bit=0; for my $list (@lists) { for my $item (@$list) { $tally{$item} |= 1<<$bit; } ++$bit; } # Show which items were in all lists my $all = (1<<$bit)-1; print "There were $bit lists. Items found:\n"; for my $item (sort {$a<=>$b} keys %tally) { print "Item $item: "; if ($tally{$item} == $all) { print "in *all* lists\n"; next; } my @present; for ($bit=0; $bit<@lists; $bit++) { push @present, $bit if $tally{$item} & 1<<$bit; } print "in lists: ", join(", ", @present), "\n"; } $ perl lists.pl LIST 0: 1, 3, 4, 6, 8, 8, 9, 9, 10, 10, 11, 11, 11, 13, 14 LIST 1: 0, 0, 0, 0, 0, 3, 3, 3, 5, 6, 8, 11, 11, 15, 15 LIST 2: 0, 1, 3, 4, 6, 6, 6, 8, 8, 9, 9, 9, 12, 13, 15 LIST 3: 1, 4, 4, 5, 5, 6, 8, 8, 8, 8, 9, 9, 12, 12, 13 There were 4 lists. Items found: Item 0: in lists: 1, 2 Item 1: in lists: 0, 2, 3 Item 3: in lists: 0, 1, 2 Item 4: in lists: 0, 2, 3 Item 5: in lists: 1, 3 Item 6: in *all* lists Item 8: in *all* lists Item 9: in lists: 0, 2, 3 Item 10: in lists: 0 Item 11: in lists: 0, 1 Item 12: in lists: 2, 3 Item 13: in lists: 0, 2, 3 Item 14: in lists: 0 Item 15: in lists: 1, 2

It's a pretty handy trick for comparing multiple lists. Of course, it doesn't keep track of how many times items are found in each list. Normally, though, that's not an issue. If you want to do something with *only* the items in all lists *and* you want to keep some data about the items, such as the number of times they appear in a list, or an associated data structure, you can do it more like:

.... oops! my son is ready for school, this is unfinished code, so I'll fix it when I get to work:

#!/usr/bin/perl use strict; use warnings; use 5.10.0; # Build a few lists my @lists; for my $i (0 .. 1+int(3*rand)) { # several lists to compare push @{$lists[$i]}, int 16 * rand for 1 .. 15; print "LIST $i: ", join(", ", @{$lists[$i]}), "\n"; } # Track the items found in each list, including the order in # which they appear my %tally; for my $listnum (0 .. $#lists) { # Get a list my $list=$lists{$listnum}; for my $itemnum (0 .. $#$list) { # Get next item from the list my $item = $$list[$itemnum]; # Each item slot (value in list) gets an array, # where each slot is associated with a list. # In that array is an array of indexes into the # list where each item is found push @{$tally{$item}[$listnum]}, $itemnum; } } # Show which items were in all lists my $all = (1<<$bit)-1; print "There were $bit lists. Items found:\n"; for my $item (sort {$a<=>$b} keys %tally) { print "Item $item: "; if ($tally{$item} == $all) { print "in *all* lists\n"; next; } my @present; for ($bit=0; $bit<@lists; $bit++) { push @present, $bit if $tally{$item} & 1<<$bit; } print "in lists: ", join(", ", @present), "\n"; }

Update: OK, here's the finished version:

$ cat lists_2.pl #!/usr/bin/perl use strict; use warnings; use 5.10.0; # Build a few lists my @lists; for my $i (0 .. 1+int(3*rand)) { # several lists to compare push @{$lists[$i]}, int 16 * rand for 1 .. 15; print "LIST $i: ", join(", ", @{$lists[$i]}), "\n"; } # Track the items found in each list, including the order in # which they appear my %tally; for my $listnum (0 .. $#lists) { # Get a list my $list=$lists[$listnum]; for my $itemnum (0 .. $#$list) { # Get next item from the list my $item = $$list[$itemnum]; # Each item slot (value in list) gets an array, # where each slot is associated with a list. # In that array is an array of indexes into the # list where each item is found push @{$tally{$item}[$listnum]}, $itemnum; } } # Show which items were in all lists my $numlists = @lists; my $all = (1<<$numlists)-1; print "\nThere were $numlists lists.\n\n"; for my $item (sort {$a<=>$b} keys %tally) { if ($numlists == grep { $_ } @{$tally{$item}}) { print "Item $item in all lists:"; for my $idxlist (0 .. $#lists) { print " list $idxlist: ", join(", ", @{$tally{$item}[$id +xlist]}), "\n"; } print "\n"; } $ perl lists_2.pl LIST 0: 5, 6, 9, 13, 3, 0, 1, 2, 5, 5, 10, 14, 15, 8, 4 LIST 1: 11, 5, 2, 12, 3, 4, 9, 13, 14, 14, 9, 15, 5, 2, 6 LIST 2: 6, 0, 7, 12, 11, 2, 13, 8, 14, 8, 13, 5, 0, 1, 7 LIST 3: 3, 14, 14, 11, 7, 10, 12, 8, 6, 15, 5, 6, 14, 6, 11 There were 4 lists. Item 5 in all lists: list 0: 0, 8, 9 list 1: 1, 12 list 2: 11 list 3: 10 Item 6 in all lists: list 0: 1 list 1: 14 list 2: 0 list 3: 8, 11, 13 Item 14 in all lists: list 0: 11 list 1: 8, 9 list 2: 8 list 3: 1, 2, 12

...roboticus

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

Replies are listed 'Best First'.
Re^2: How to compare 3 lists
by shammow (Novice) on Oct 25, 2012 at 18:47 UTC

    Wow, thanks. I was staring at a blank screen needing inspiration, but that should be perfect.