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

Hi, all.
Evrybody know, that if I do this:
my $str='1,zxc,2,asd,3,qwe'; my %hash=split /\,/, $str;
then I've get hash with keys 1,2,3 and values zxc,asd,qwe.
But I need to sort this hash by keys and do something.
Of course I can do
foreach my $key (sort keys %hash) { #do something... };

But I've intrested in another optimization - can I do this without of %hash in foreach and not lost access to key and it's value

Thank you for your time and attention.
Alexander.

Replies are listed 'Best First'.
Re: split and sort functions
by bart (Canon) on Apr 21, 2006 at 13:28 UTC
    Can I assume your original data is sorted, and you really just want to maintain the original order? Then you want something like Tie::IxHash. There are several of those modules, like Tie::Hash::Indexed, which is written in XS (the former is in Pure Perl) and thus, probably faster.

    Perhaps you do want your hash to be automatically sorted. In that case, you might be interested in something that works like a hash, but internally keeps the keys sorted, something like a binary search tree. I don't know immediately of something that you could use instead of a hash, but perhaps, assuming your hash keys are always integers, a Judy Array might work for you. There's an XS implementation for Perl in Tie::Judy. The docs there say: "the keys here are in bit-wise SORTED order".

Re: split and sort functions
by japhy (Canon) on Apr 21, 2006 at 14:03 UTC
    It sounds like you want to do
    my $str = "this,ok,that,cool,free,dom"; for my $key (sort keys (%hash = split /,/, $str)) { ... }
    which probably isn't allowed (you probably get an error message like "argument to keys must be hash, not list assignment"). Long story short, to get this done, you'd have to do something sneaky like:
    for my $key (sort keys %{ $hashref = { split /,/, $str } }) { ... }
    or
    for my $key (sort keys %{ +{%hash = split /,/, $str} }) { ... }
    But that's a bit of kludge for something that should really be two distinct processes. Unless you don't really need the hash, in which case you can just do:
    for my $pair (sort($str =~ /([^,]*,[^,]*)(?:,|$)/g)) { my ($k, $v) = split /,/, $pair; ... }

    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
Re: split and sort functions
by blazar (Canon) on Apr 21, 2006 at 13:24 UTC

    Hashes are called like this because despite being really (from the conceptual pov) associative arrays, they're actually implemented by means of a hashing function. This makes them intrinsically unordered objects. You have either to sort yourself or resort to an object that behaves like a hash preserving order, possibly at the expense of some efficiency: check for example Tie::Hash::Sorted.

    Update: I hadn't understood the bit about "without of %hash in foreach". If now I understand it correctly... all in all this would be perfect for Perl 6's zip()...

Re: split and sort functions
by Roy Johnson (Monsignor) on Apr 21, 2006 at 14:19 UTC
    Perhaps you don't want a hash at all. Or perhaps you want to do something with the elements in their original order before you build a hash. In either case, start by extracting all the pairs, then splitting them into their components. Then do whatever you're going to do with them (including, perhaps, inserting them in a hash).
    use strict; use warnings; my $str='1,zxc,2,asd,3,qwe'; foreach ( $str =~ /([^,]+,[^,]+)/g ) { my ($k, $v) = split /,/; print "Do something with key $k and value $v\n"; }
    Update:
    I had a "duh" moment. You can, of course, extract the elements like so:
    foreach (my ($k,$v) = $str =~ /([^,]+),([^,]+)/g) {

    Caution: Contents may have been coded under pressure.
      Thank you all for answers. I realy don't need hash :). Regards, Alexander.
Re: split and sort functions
by ptum (Priest) on Apr 21, 2006 at 13:23 UTC

    You don't really need that backslash in your split statement -- a comma isn't a metacharacter. But that probably isn't the kind of optimization you're looking for. :)

    Then again, I'm not really clear as to what you are asking. Can you rephrase your question?


    No good deed goes unpunished. -- (attributed to) Oscar Wilde