in reply to Retaining hash order with Config::General?

While I am not familiar with the Config::General you are working with, a fundamental property of hashes is that order of key-value pairs should be irrelevant. Do you care about the order for some internal reason, or is this about the order in the saved file?

Update: The Config::General->save_file method calls the _store method, which in turn traverses the hash with a keys method on line 1252. There also appears to be a save sorted version of the same loop, which you can use by setting the -SaveSorted flag and will reliably output your keys in alphabetical order. It still doesn't change the fact that a hash's order should be irrelevant.

Update 2: Forward/backward slash typo. Thanks Arunbear.

  • Comment on Re: Retaining hash order with Config::General?

Replies are listed 'Best First'.
Re^2: Retaining hash order with Config::General?
by bart (Canon) on Dec 15, 2008 at 17:31 UTC
    Update: The Config::General->save_file method calls the _store method, which in turn traverses the hash with a keys method on line 1252.

    And thereabout is where the problem lies. The module takes extra care of preserving ties on the data (thus on the order, if tied to Tie::IxHash), up to calling this method, store_. And there, it foolishly throws it away:

    sub _store { # # internal sub for saving a block # my($this, $level, %config) = @_; ... }
    Ouch.

    This assignment of the sorted hash data to an ordinary hash %config is where the semi-random order comes from — only to traverse it later on.

    Tieing to any hash is useless this way.

    I'd call that a bug. I think it'd be better if _store accepted a hashref instead of flat data, to traverse the data tree.

      Thank you all for your replies.

      I will see if I can hack this by passing a hash ref or something. Christmas vacation coming up ;-)

Re^2: Retaining hash order with Config::General?
by Anonymous Monk on Dec 15, 2008 at 16:49 UTC
    yes, I need it for internal purposes. Some of the entries in the file are listed to the user as available options for run-time configuration. from the user perspective, it looks silly when the menus change order for no apparant reason. Granted, I could force an alphabetical listing, or even maintain a separate sort list. Alphabetical is essentially random in this case, since anything except the structured order will not appear sane.. Cheers

      "Alphabetical is essentially random in this case, since anything except the structured order will not appear sane "

      So use a label field and sort by that. Consider a database. I don't care how the database stores the internals, only that I can retrieve that data. If i want to sort that data, i have to make sure that the data is sortable in a meaningful way. Personally, i would not store information into a Config file that would be better suited in a database, such as data that changes often. YMMV.

      jeffa

      L-LL-L--L-LL-L--L-LL-L--
      -R--R-RR-R--R-RR-R--R-RR
      B--B--B--B--B--B--B--B--
      H---H---H---H---H---H---
      (the triplet paradiddle with high-hat)
      

        I see your point(s). However this data doesn't change often, and is fairly complex (ie would involve a number of tables with one-to-many, m-to-o and o-to-o relationships. In short, a database solution would require signifficant development time as opposed to a few lines of Config::General creating a fully usable complex data structure that needs little or no maintenance and is simple to backup and modify.

        Second, what I need to retain here is only the order at which the keys were inserted. This works perfectly via the -Tie parameter when reading, just not when writing (see bart's post below).

        So by your database analogy, I need "SELECT * FROM tbl" -- no sorting or other magic, if I could simply keep the tie during save I'd be happy ;-)

      Based on the code I've seen, you are best off just resorting the hash keys to the order you want just prior to display. Alphabetical is easiest, but you can resort the hash order with a custom test like this:

      #!/usr/bin/perl use strict; use warnings; use Config::General qw{ParseConfig}; use Tie::IxHash; my $file = 'test.cfg'; tie my %CFG, "Tie::IxHash"; %CFG = ParseConfig( -ConfigFile => $file, -Tie => "Tie::IxHash", ); print "When freshly loaded:\n"; print join "\n", keys %CFG; print "\n\n"; (new Config::General( -ConfigHash => \%CFG, -Tie => "Tie::IxHash", ))->save_file($file); # SaveConfig appears "equally bad" at this.. %CFG = ParseConfig( -ConfigFile => $file, -Tie => "Tie::IxHash", ); print "When saved & reloaded:\n"; print join "\n", sort {myfunction($a, $b)} keys %CFG; sub myfunction { my($key1, $key2) = @_; if (substr($key1,length($key1)-1,1) == substr($key2,length($key2)- +1,1)) { return ($key1 cmp $key2); } else { return (substr($key1,length($key1)-1,1) cmp substr($key2,lengt +h($key2)-1,1)); } }