http://qs1969.pair.com?node_id=403873

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

Fellow Monks, I am having an issue with sorting an non-standard set of elements, a list of Software Levels for multiple elements in our network. The network Elements are in the format Major.Minor.Load(LoadVersion) and meet the regex
/\d+\.\d+\.\d+\w*/

in formatting. It might just be a very long week, but I am having an issue finding the all ecompassing way to sort these. *NOTE the final \w* is a letter that may or may not be there (hence the *) and should be sorted in a
my @sort = sort($prevLoadVersion, $currLoadVersion); my $max = shift @sort;
fashion.


Tradez
"Every official that come in
Cripples us leaves us maimed
Silent and tamed
And with our flesh and bones
He builds his homes"

- Zach de la Rocha

Replies are listed 'Best First'.
Re: Sorting non-standard elements
by fglock (Vicar) on Oct 29, 2004 at 21:22 UTC

    CPAN is your friend. Use Sort::Versions - from the docs:

    use Sort::Versions; @l = sort { versioncmp($a, $b) } qw( 1.2 1.2.0 1.2a.0 1.2.a 1.a 02.a ) +;
Re: Sorting non-standard elements
by Fletch (Bishop) on Oct 29, 2004 at 19:18 UTC

    See perldoc -q sort. Use capturing parens to extract the different chunks and compare the corresponding chunks. Use a Schwartzian transform so you don't wind up pulling items apart multiple times.

Re: Sorting non-standard elements
by artist (Parson) on Oct 29, 2004 at 19:17 UTC
    @data = <DATA>; @sorted = sort { ($a <=> $b) || ($a cmp $b) } @data; foreach (@sorted){ print; } __DATA__ 1.1.a 20.2 2.3.a 2.3.d 2.2 2.1.a 9.1.e 9.1.f 10.1.b 20.2 15.2.c
Re: Sorting non-standard elements
by theorbtwo (Prior) on Oct 29, 2004 at 21:08 UTC

    Let's start with a re-phrase. A software version consists of three numeric parts, plus any number of additional characters. The three numeric bits are sorted in numeric order, and the character bits in character order.

    Here's one way to do it: Convert the human-readable version number into a cannonical form that's easy to compare. Then sort. Then convert them back into human-readable version numbers. A first attempt looks like this:

    sub cannonify { my $ver = shift; $ver =~ /(\d+)\.(\d+)\.(\d+)(\w*)/ return chr($1).chr($2).chr($3).$4; } sub uncannonfiy { my $ver = shift; return ord(substr($ver, 0, 1)) . '.' . ord(substr($ver, 1, 1)) . '.' . ord(substr($ver, 2, 1)) . . substr($ver, 3); }

    You will note that this isn't very efficent. Try combining it with the Schwartzian transform that other people have pointed you out, and without specifiying a comparator function to your sort call (to sort lexographicly).

    Also, if you just want to find the maximum version, don't use sort, which has to do much more work, instead, use List::Util's max().


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

Re: Sorting non-standard elements
by TedPride (Priest) on Oct 29, 2004 at 19:39 UTC
    I'm not exactly sure what you want, but I'm assuming you're looking to sort first by word, then by each of the three numbers. Something like the following might work:
    use strict; use warnings; my @arr; while (<DATA>) { chomp; m/(\d+)\.(\d+)\.(\d+)(\w*)/; $arr[$#arr+1] = [$4,$1,$2,$3,$_]; } @arr = sort {@$a[0] cmp @$b[0] or @$a[1] <=> @$b[1] or @$a[2] <=> @$b[ +2] or @$a[3] <=> @$b[3]} @arr; for (@arr) { print @$_[4]."\n"; } __DATA__ 1.2.3YABBA 4.2.7 3.2.1DABBA 9.1.3 4.5.1DOO 12.19.47