in reply to Sorting a hash of IP addresses?

Well, you can't sort a hash, as hashes are always unsorted. But presuming that you want to process the hash values in sorted order, you can do something like this:

foreach (sort ipsort keys %ip) { # each sorted key will be in $_ } sub ipsort { my @a = split /\./, $a; my @b = split /\./, $b; return $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3]; }

Update: As hakkr points out below you can certainly have a variable that looks and acts like a sorted hash by using Tie::IxHash. But being pedantic, it isn't actually a hash. It's a Perl object (actually a reference to an array) that you can interact with via an interface that looks very much like a hash :)

And, anyway, it sorts by insertion order, which isn't what the OP wanted.

--
<http://www.dave.org.uk>

"The first rule of Perl club is you do not talk about Perl club."
-- Chip Salzenberg

Replies are listed 'Best First'.
Re: Re: Sorting a hash of IP addresses?
by Hofmator (Curate) on Dec 05, 2001 at 15:45 UTC

    may I add that a Schwartzian Transform might help speeding up the sort. Then the split doesn't have to be done over and over again. This leads to

    my @sorted_keys = map {$_->[0]} sort {$a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] || $a->[3] <=> $b->[3] || $a->[4] <=> $b->[4]} map { [ $_, split /\./ ] } keys %ip; foreach (@sorted_keys) { # do something }

    Instead of splitting one could also use the MZSanford's suggestion and sort on a zeropadded string in the transformation.

    Update:
    The code above was written to resemble as closely as possible davorg's original subroutine. For better solutions (in terms of performance) see blakem's and davorg's responses to my post.

    -- Hofmator

      You could simplify the comparison by upgrading your transformation. Instead of just splitting it into four pieces, why not convert it to dotless notation (i.e. the decimal representation of the underlying 32 bits). Comparing ips in dotless notation is trivial... a single <=> is all you need.

      In general it makes sense to push the complexity into the transformation since it is only done once for each element in the list.

      Something like: (warning untested)

      my @sorted_keys = map {$_->[0]} sort {$a->[1] <=> $b->[1]} map { [ $_, unpack("N",pack("C4",split(/\./,$_))) ] + } keys %ip;
      * I borrowed the ip => dotless conversion from Fletch's golf entry

      -Blake

        And let's not forget that sorting IP addresses was the canonical example of the packed default sort that Uri Guttman and Larry Rosler used in their most excellent paper on Perl sorting. Changing their code a little gives us this:

        @sorted_keys = map substr($_, 4) => sort map pack('C4' => /(\d+)\.(\d+)\.(\d+)\.(\d+)/) . $_ => keys %ip;
        --
        <http://www.dave.org.uk>

        "The first rule of Perl club is you do not talk about Perl club."
        -- Chip Salzenberg

Re: Re: Sorting a hash of IP addresses?
by hakkr (Chaplain) on Dec 05, 2001 at 15:06 UTC
    my %ipsort; use Tie::IxHash; tie %ipsort, "Tie::IxHash";

    This will give you a hash sorted by insertion order.