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

I have an array which is a subset of a larger array, the larger array is sorted the smaller is not. What is an elegant, perly way to sort the elements in the smaller array into the same order they are in the larger?

Right now I dump the smaller array into a hash, and basically go through the larger in order and only keep the values that exist in the smaller - I'm thinking there's gotta be a better way, though. Monks?

  • Comment on sort an array according to another array

Replies are listed 'Best First'.
Re: sort an array according to another array
by halley (Prior) on Jun 03, 2003 at 20:19 UTC

    Do benchmarking on your method, and any other method you come across. Here's my idea, but I'm sure there's others.

    If @L's elements are unique, and @S is truly a subset of @L, then one way would be:

    $i = 0; %L = map { $_ => $i++ } @L; @Ss = sort { $L{$a} <=> $L{$b} } @S;

    It requires more intermediary storage, but perhaps a simpler operation.

    Test results:

    @L = qw(one two three four five six seven eight); @S = qw(eight one five three); @Ss = qw(one three five eight);

    --
    [ e d @ h a l l e y . c c ]

Re: sort an array according to another array
by sauoq (Abbot) on Jun 03, 2003 at 21:51 UTC

    The problem is that you are stuck with lookups into the larger array and that's O(n). Your best choice if you have to do this a lot and the larger array isn't so large that memory usage would make it unfeasible, would be to make a hash keyed by the elements in the larger array where the values are those elements' indexes. Otherwise, what you are doing is pretty reasonable.

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: sort an array according to another array
by tye (Sage) on Jun 03, 2003 at 23:10 UTC

    If the smaller list is a lot smaller and you want to do the same sort a lot more often than you want to change the large list, then things might run faster if you build a database (such as DB_File) mapping each member of the large list to its position (similar to other methods in this thread but using a tied hash).

    The tied hash would not need to be built each time you sort. Otherwise, you've got to go through the large list linearly every time in order to build the large hash, which probably means that things won't run much faster than your current method.

                    - tye
Re: sort an array according to another array
by Anonymous Monk on Jun 03, 2003 at 20:19 UTC
    Well, unless something about your sorting mechanism is weird, sorting the subset by the same rules as the superset should produce a properly sorted set.
      The sorted array is produced by a combination of several external applications and is the ultimate result of an initial data set of several hundred megs - a few weeks of work. The sorting of the smaller set is something I need to do quite often, there is just no way to repeat this process every time (nor any need).
Re: sort an array according to another array
by jaa (Friar) on Jun 04, 2003 at 08:02 UTC
    If I read your question correctly, you have something like this?
    use strict; my @universe = sort complex_sort (1001 ... 5000); my @random_offsets = (503,1083,260,5,101); my @subset = @universe[@random_offsets]; print "unsorted subset: @subset\n"; # Want subset in same complex_sort order as @universe # Can do a quick sort on the index, as @universe # is already in the order we want! # Option 1: @subset = @universe[sort( {$a <=> $b} @random_offsets)]; print "sorted subset: indicies: @subset\n"; # But if unable to determine the universe indicies # in advance, we might have to sort the subset once # Option 2: @subset = sort complex_sort @subset; print "sorted subset: complex_sort @subset\n"; sub complex_sort { return $b <=> $a; }
    Regards

    Jeff

Re: sort an array according to another array
by wufnik (Friar) on Jun 04, 2003 at 08:58 UTC
    here is something small you might like to try if halley's hash above proves too chunky for your ZX81.

    the following will deposit the sorted subset in @uS; the memory consumued will be of the order of the size of elements in the unsorted list. so it will be kind to machines with less RAM.

    if the unsorted list is short, the fact that the only a few grep's will be employed will make the solution reasonable in time too.

    my @S=("tilly","zaxo","sauog","enlil","castaway","wufnik"); my @U = ("castaway","zaxo","wufnik","enlil"); my %uS = map { $tmp = $_; $tmp => grep { $S[$_] =~ /^$tmp$/ } (0..$#S) + } @U; @uS = sort { $uS{$a} <=> $uS{$b} } @U; print join "\n", @uS;
    which produces

    zaxo

    enlil

    castaway

    wufnik

    from the unsorted list above. it freely admit it won't always be the fastest solution, but it will be kind, at least to those who like map & grep.

    ...wufnik

    -- in the world of the mules there are no rules --
Re: sort an array according to another array
by aquarium (Curate) on Jun 04, 2003 at 01:55 UTC
    i take it than when you traverse the larger array that you're looping through the index number. use that number as the key when you're creating the small hash. then a simple sort keys %small_hash gives them to you in order.
Re: sort an array ... another array (not reading closely enough)
by Enlil (Parson) on Jun 04, 2003 at 09:35 UTC
    update: Is there an echo in here? ACK!! i just reread the start of this thread, and realized it was exactly the same thing as the OP is doing in the first place.

    Here is my take on your problem. Take the Smaller array and create a hash on what exists in it. Then go through the larger array and grep the elements that exist in the hash created with the smaller array. And in the end it is sorted in the order things appear in the large array and contains only the things in the smaller array, as follows:

    use strict; use warnings; my @sorted = ("a" .. "z", "A" .. "Z"); my @needs_sorting = ( qw (P l E a S e s O r T) ); my %S; @S{@needs_sorting} = (); my @is_sorted = grep { exists $S{$_} } @sorted; print join "|",@is_sorted;
      superb. much nicer than my way of getting the subset's sorted order. and still no megahash for the sorted set.

      ...wufnik

      -- in the world of the mules there are no rules --
Re: sort an array according to another array
by zentara (Cardinal) on Jun 04, 2003 at 15:41 UTC