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

I'm having trouble with this problem sorting the IP address in increasing numeric order. If the first octet is the same, sort by the second octet, if the second octet is the same sort by the third octet, etc. IP addresses have a different number of digits between the periods so you will have to split the data up.

An example is:

I can sort it by increasing to decreasing number but not by each octet.
The answer should be:

by Thank You

Replies are listed 'Best First'.
Re: sorting ip octets
by Tanktalus (Canon) on Feb 16, 2005 at 18:59 UTC


    my @ips = qw( ); my @sorted = map { $_->[1]; } sort { $a->[0] cmp $b->[0] } map { [ sprintf("%03d.%03d.%03d.%03d", split /\./), $_ ] } @ips; print map { $_.$/ } @sorted;
    Is that reasonable? (I did test it - it seems to work for the example above.)

    Update: Change the @sorted line to this also works:

    my @sorted = map { $_->[1]; } sort { $a->[0] <=> $b->[0] } map { [ sprintf("%03d%03d%03d%03d", split /\./), $_ ] } @ips;

    This one uses numerical comparison... not really sure which one would be faster. I like the first one better if only because it looks to me like I'm doing what I say I'm doing, where as this one looks more like I'm faking it.

      thanks it works but I open it from a txt file and read it line by line.

      thank you for the help

        Normally, we recommend reading files and dealing with them line-by-line. That doesn't really work for sorting. So just read your text file into @ips, chomping as appropriate, and then sort as above. All I was helping you with was the sorting of ip octets, not the reading of a file or writing to a file. Those are left as an excersise for the reader. (I never knew how much fun it was to say that until I left university... :-})

Re: sorting ip octets
by demerphq (Chancellor) on Feb 16, 2005 at 18:54 UTC

    Convert the ips to integers (there are built in functions for this) and then sort the integers, then convert them back to text form ips.


      I have no problem in converting them to integers, is going from column to column.

        Have a look at the Socket module which has tools for converting IP addresses into their integer form and back. inet_aton() and inet_ntoa() come to mind...


Re: sorting ip octets
by eclark (Scribe) on Feb 16, 2005 at 21:29 UTC

    Pack it!

    sort { unpack('N',pack('C4',split(/\./,$a))) <=> unpack('N',pack('C4',split(/\./,$b))) } @list

      If you dont like using pack. Or you have another type of data in the future, remember than cmp and <=> return 0 if they are the same. See below.

      sort { my @a = split(/\./, $a); my @b = split(/\./, $b); $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3] } @list

        You should read the FMTYWTK about sort...

        Doing it this way will be really slow on large data sets since you are doing lots of splits...

        Note, if you apply the FMTYWTK technique then you get about the same as my post...

Re: sorting ip octets
by Animator (Hermit) on Feb 16, 2005 at 20:14 UTC

    A rather late reply, posted only to demonstrate TIMTOWTDI.

    { my $x; @ips = map { $_->[0] } sort { $x=0; $x = $a->[$_] - $b->[$_] and last for (1 .. 4); $x; } map { [ $_, split /[.]/ ] } @ips; }

Re: sorting ip octets
by TedPride (Priest) on Feb 16, 2005 at 19:13 UTC
    Or, a somewhat more readable version:
    my @ips = <DATA>; $_ = sprintf("%03d.%03d.%03d.%03d", split /\./) for (@ips); @ips = sort @ips; $_ = sprintf("%d.%d.%d.%d", split /\./) for (@ips); print join "\n", @ips; __DATA__
    Basically, you convert to fixed width (3 chars per octet), sort, and convert back to the original unpadded numbers.