Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked

Re: how to find combine common elements of an array?

by jaredor (Priest)
on Apr 06, 2011 at 05:23 UTC ( [id://897662] : note . print w/replies, xml ) Need Help??

in reply to how to find combine common elements of an array?

Thanks to inspiration from BrowserUK and wind (though I still have a ways to go before I grok their replies) I think I have something to share if only for terseness.

#!/usr/bin/env perl use strict; use warnings; use List::Util(qw(min)); use List::MoreUtils(qw(uniq)); my @array = map {[split]} ("11 12", "11 13", "9 8 7", "3 4", "11 4"); my %v2g = (); my %g2v = (); for my $i (@array) { my @sg = sort {$a<=>$b} uniq grep {defined} (@v2g{@$i}, min @$i); my $sg = shift @sg; $g2v{$v2g{$_} ||= $sg}->{$_}++ for @$i; for my $j (@sg) { @v2g{keys %{$g2v{$j}}} = ($sg) x keys %{$g2v{$j}}; @{$g2v{$sg}}{keys %{$g2v{$j}}} = values %{$g2v{$j}}; delete $g2v{$j}; } } print join ("\n", map {join " ", sort {$a<=>$b} keys %$_} values %g2v) +, "\n";


3 4 11 12 13
7 8 9

About the only thing really new to add to the discussion is that this problem seems to be equivalent to finding the disjoint subgraphs of a vertex adjacency matrix of a non-directed graph. So there's probably an algorithm out there that puts this code's efficiency to shame.

Replies are listed 'Best First'.
Re^2: how to find combine common elements of an array?
by jaredor (Priest) on Apr 09, 2011 at 22:37 UTC

    Here's a better version. I was going to wipe my scratchpad and saw that I could do one thing better ... then another ... then realized that there didn't need to be an identified element per subgraph...

    #!/usr/bin/env perl use strict; use warnings; use List::MoreUtils(qw(uniq)); my @array = map {[split]} ("11 12", "11 13", "9 8 7", "3 4", "11 4"); my %g = (); for my $i (@array) { my @v = map {@$_} uniq map {$g{$_} or [$_]} @$i; @g{@v} = (\@v) x @v; } print join ("\n", map {join " ", sort {$a<=>$b} @$_} uniq values %g), +"\n";

    Same output as above, although now the ordering of the lines is fortuitous. (But obviously can be set, if desired, by another sort.)

    Hopefully it's a bit more clear that, since each iteration creates a complete set of associated vertices, the process finds the sets of vertices of the connected subgraphs of an arbitrary graph.

    (Please pardon the enthusiasm, this is the kind of stuff I like to think about.)

      You are too kind Miller, and you flatter me to think that my one-off is worth remembering!

      Such kindness deserves a little extra effort, and so here is a little defensive coding to go along with the main thrust of the code: If you are not sure that there will be no duplication of numbers in any given list, then in general you will not be able to collapse the list to a minimal set of elements. For example, if you just have a single list, '3 3 4', then the code will return a list of '3 3 4', not '3 4'. (However, if the currently duplicated element has been seen before, there will be no duplication.) This potential problem is countered by another uniq statement just before the assignment to @v and is (to my taste) slightly ugly, but necessary if you want to run this on slightly muddier data sets.

      You must be right about the title to this post being too obscure, since I presume you are one of only the six who upvoted this post. With 225 XP left to go, my long, hard slog to curate will only happen by me personally upvoting other posts in "Recent Threads." ;-)

      Thank you again for thinking of this post. Double thanks for crediting me elsewhere!


      P.S. I've only run the code through my somewhat fallible wet-ware, so caveat coder. One of these days I'll be an active Perl coder again and I won't make as many educated guesses as I do now. (Project Euler was my perl outlet for a while, but that site is down now. Gee, I can't recall, did I use the same password for PM as for PE? I hope it wasn't the other way around before, say, May 20th, 2009 ;-)

      P.P.S. EDIT Oops, this was supposed to be a reply to wind.

      I recently came by a question on stackoverflow that mirrored this thread: Merging a list of lists of lists

      It's unfortunate that this thread has such a poor title, as I doubt google is going to find it very often. But I definitely remembered your solution and was able to search through all my past posts to find it.

      I'm still in awe of your use of uniq on array references, so just wanted you to know that I referenced your code. :)

      - Miller