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

Hi all,

I have been trying to figure out how to create a hash of a range of another hash...

I've created a hash1 with keys serialized from 1 to ...
And I would like to create hash2 with a range.. ie: keys 5..10 from hash1.
Of course the START and END of the range would be variable.

Could I get a kick in the right direction please, Thanks!

-Enjoy
fh : )_~

Replies are listed 'Best First'.
Re: A hash of a range of another hash
by ELISHEVA (Prior) on Sep 11, 2009 at 06:56 UTC

    Sounds like you need to work with hash and array slices. But you'll also have to decide what you mean by "first key", "second key", etc. Hash keys don't have a meaningful ordering of their own. You have to impose the order on them from the outside:

    # put keys in ascending asciibetical order my @aKeys = sort keys %hSomeHash; #put keys in descending asciibetical order @aKeys = sort { $b cmp $a } keys %hSomeHash; # @var{keys} is a hash slice # @var[number range] is an array slice @hSomeHash{@aKeys[0..4]}; #values for first 5 sorted keys @hSomeHash{@aKeys[5..9]}; #values for next 5 sorted keys # or to create a hash from the a subset of keys my %hash0_4 = map { $_ => $hSomeHash{$_} } @aKeys[0..4]; my %hash5_9 = map { $_ => $hSomeHash{$_} } @aKeys[5..9];

    For more about array and hash slices, see perldata. You may also find sort and map helpful.

    Best, beth

    Update: added sample code for using array slice to create a subset hash

Re: A hash of a range of another hash
by ikegami (Patriarch) on Sep 11, 2009 at 07:12 UTC
    my %src = ( 0=>'a', 1=>'b', ... ); my ($s,$e) = (5,10); my %dst; @dst{ $s..$e } = @src{ $s..$e };
    It's really weird that you're using hashes here and not arrays. It's a really inefficient and trickier way of storing ordered results. If you did switch to an array, the above would look like
    my @src = ( 'a', 'b', ... ); my ($s,$e) = (5,10); my @dst = @src[ $s..$e ];
Re: A hash of a range of another hash
by Anonymous Monk on Sep 11, 2009 at 06:43 UTC
    You should show an example structure
    #!/usr/bin/perl -- use strict; use warnings; my ( $left, $right ) = ( 5, 8 ); my (%f) = map { ($_) x 2 } 1 .. 10; my (%r) = map { $_ => $f{$_} } $left .. $right; use Data::Dumper; print Dumper( \%f, \%r ); __END__ $VAR1 = { '6' => 6, '3' => 3, '7' => 7, '9' => 9, '2' => 2, '8' => 8, '1' => 1, '4' => 4, '10' => 10, '5' => 5 }; $VAR2 = { '8' => 8, '6' => 6, '7' => 7, '5' => 5 };
Re: A hash of a range of another hash
by grizzley (Chaplain) on Sep 11, 2009 at 06:53 UTC

    I think in any case you must scan first hash and store all keys' information (how many keys, what values, etc., hard to say what you need exactly), and then build another hash. To do this, use

    keys(%hash1)

    or

    while(each($key, $value) = %hash1) { }

    loop.

Re: A hash of a range of another hash
by Anonymous Monk on Sep 11, 2009 at 17:02 UTC
    Hi all,

    Thanks for the input!

    The following is the answer I was looking for and was provided by an Anonymous Monk.
    Thank you!

    my (%r) = map { $_ => $f{$_} } $left .. $right;

    And what I am doing!
    "Brain Cell Exercise Project 11"

    Ultimately the goal is to page (or line by line) up and down through the list, and enter a number to select a file.
    Just have not gotten that far yet.

    This is a Brain Cell Exercise so, Any pointers to cleaner, neater, slicker methods would be VERY WELCOME and will be appreciated!

    -Enjoy
    fh : )_~

      What do you think of this?
      #!/usr/bin/perl # # BCE Project 11 # use strict; use warnings; my $Directory = 'c:/data'; # Any directory containing files will do my @Farray; # Array of found files my $Pagelen = 20; # Page length (Number of items to print) sub printlist { my ( $Start, $End, @array ) = @_; $End = $End > @array ? @array : $End; printf "%s: %s\n", $_, $array[ $_ - 1 ] foreach $Start .. $End ; } ## end sub printlist opendir( DIR, $Directory ) || die "can't opendir $Directory: $!"; @Farray = sort grep { -f "$Directory/$_" } readdir(DIR); closedir DIR; print "Itemcount= scalar @Farray\n"; while (1) { print "Enter number: "; my $Input = <STDIN>; last if $Input =~ m/^q/i; printlist $Input, $Input + $Pagelen - 1, @Farray; } ## end while (1)

      To start with we have done away with the hash-based storage of the filenames. The result of the grep is sorted and directly stored in an array.

      The while loop has been cleaned-up as well: no need to chomp your input here. Perl is not bothered by the "\n" at the end of the input when used in numerical context.

      I have used a statement modifier rather than an if .. then ... construct for the last in the loop. Personally I find it clearer and more expressive, but that is just a personal thing.

      The subroutine call now adds the array with the filenames at the end of the parameter list. Together with some other changes in the printlist subroutine it means that the subroutine does not rely on any variables which exist outside of itself. This is something I have found to be very handy: now the subroutine can be used anywhere in your program or --if put in a librarymodule-- even in other programs. Also, there is now no "action at a distance" possible: otherwise a certain source of difficult to trace bugs!

      The printlist subroutine is now very simple: it uses no variables other than those necessary to store its parameters. The ... ? ... : ... construct is a very compact way of putting boundaries on the value of a variable and if necessary it can be nested if more conditions need to be checked.

      Note that the array index needs to be reduced by one, as arrays are zero-based and your program uses a one-based approach.

      The program still has room for improvement: for instance if a really huge array of filenames needs to be processed, then it will be faster and less memory intensive if rather than the array itself, only a reference to the array is put in the parameter list. Of course you will have to dereference this array-reference where needed in the printlist subroutine! For small arrays (up to many thousands of elements), there is not really a big benefit in doing so however.

      CountZero

      A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: A hash of a range of another hash
by bichonfrise74 (Vicar) on Sep 11, 2009 at 17:43 UTC
    When I saw this, I tried to solve this using the code below. This is very similar to the code presented by 'Anonymous Monk'. #!/usr/bin/perl use strict; use Data::Dumper; my @range = (5 .. 8); my %hash_1 = map { $_ => $_ } (1 .. 10); my %hash_2 = map { $_ => $hash_1{$_} } @range; print Dumper \%hash_2;