Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Looping Over Hash Skips an Element?

by mmartin (Monk)
on Jan 06, 2012 at 17:44 UTC ( #946635=perlquestion: print w/ replies, xml ) Need Help??
mmartin has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I'm looping through a hash that has about 240 elements. The order of the hash defiantly matters, so I used "use Tie::IxHash;" to preserve the ordering of the hash.

So in the code below, I loop through the hash and have a counter that is set to 1 and resets when its > 8.
But for some reason it skips what should be the 9th element in the hash on each reset.

The code below contains only a small portion of the hash (the first 32 elements).

And when I execute the code it prints the following:
Gi3/1 Gi3/2 Gi3/3 Gi3/4 Gi3/5 Gi3/6 Gi3/7 Gi3/8
Gi3/10 Gi3/11 Gi3/12 Gi3/13 Gi3/14 Gi3/15 Gi3/16 Gi3/17
Gi3/19 Gi3/20 Gi3/21 Gi3/22 Gi3/23 Gi3/24 Gi3/25 Gi3/26
Gi3/28 Gi3/29 Gi3/30 Gi3/31 Gi3/32

As you can see it skips would would be the 9th element, going from Gi3/8 to Gi3/10 and so on...

I'm assuming it's somehting to do with my logic. Anyone know what I'm doing wrong?

use warnings; use strict; use Tie::IxHash; tie (my %ifSpeeds, "Tie::IxHash"); %ifSpeeds = ( "Gi3/1" => "1000000000", "Gi3/2" => "100000000", "Gi3/3" => "1000000000", "Gi3/4" => "1000000000", "Gi3/5" => "100000000", "Gi3/6" => "1000000000", "Gi3/7" => "1000000000", "Gi3/8" => "1000000000", "Gi3/9" => "100000000", "Gi3/10" => "1000000000", "Gi3/11" => "1000000000", "Gi3/12" => "1000000000", "Gi3/13" => "1000000000", "Gi3/14" => "100000000", "Gi3/15" => "1000000000", "Gi3/16" => "1000000000", "Gi3/17" => "1000000000", "Gi3/18" => "1000000000", "Gi3/19" => "100000000", "Gi3/20" => "1000000000", "Gi3/21" => "100000000", "Gi3/22" => "1000000000", "Gi3/23" => "1000000000", "Gi3/24" => "1000000000", "Gi3/25" => "100000000", "Gi3/26" => "100000000", "Gi3/27" => "100000000", "Gi3/28" => "100000000", "Gi3/29" => "100000000", "Gi3/30" => "100000000", "Gi3/31" => "1000000000", "Gi3/32" => "1000000000" ); my $counter = 1; foreach my $key ( keys %ifSpeeds ) { if ($counter <= 8) { print "$key "; $counter++; } else { print "\n"; $counter = 1; } }



Any thoughts would be much appreciated.

Thanks,
Matt

Comment on Looping Over Hash Skips an Element?
Download Code
Re: Looping Over Hash Skips an Element?
by toolic (Chancellor) on Jan 06, 2012 at 17:47 UTC
    If you want the 9th to show up in your output, you need to print it:
    foreach my $key ( keys %ifSpeeds ) { if ($counter <= 8) { print "$key "; $counter++; } else { print "$key\n"; # <-------------- $counter = 1; } }
    Update: List::MoreUtils can also be used for this:
    use List::MoreUtils qw(natatime); my $it = natatime(9, (keys %ifSpeeds)); while (my @vals = $it->()) { print "@vals\n"; }
      Hey toolic, thanks for the reply.

      Duhh haha... I guess I had a brain fart on that one..! lol thanks.

      I was just using the printing as an example. What I'm reallying trying to do is to add 8 consecutive values from the hash and place that summed value in an array.

      Using the same hash, would this be what I should do?
      my @sum_values; my $x = 0; foreach my $key ( keys %ifSpeeds ) { if ($counter <= 8) { $sum_values[$x] += $ifSpeeds{ $key }; $counter++; } else { $x++; $sum_values[$x] = $ifSpeeds{ $key }; $counter = 1; } } for (my $x = 0; $x <= $#sum_values; $x++) { print "$x) $sum_values\n"; }

      The final result (given the hash with 32 elements) would be that the array would contains 4 values, which include the following:
      [0] --> (sum of Gi3/1 to Gi3/8) [1] --> (sum of Gi3/9 to Gi3/16) [2] --> (sum of Gi3/17 to Gi3/24) [3] --> (sum of Gi3/25 to Gi3/32)
      Does all that look correct to you?


      Thanks Again,
      Matt


        How about:
        use List::MoreUtils qw(natatime); use List::Util qw(sum); use Data::Dumper; my @sums; my $it = natatime(8, (values %ifSpeeds)); while (my @vals = $it->()) { push @sums, sum(@vals); } print Dumper(\@sums); __END__ $VAR1 = [ '6200000000', '6200000000', '6200000000', '2600000000' ];
        if ($counter <= 8) { $sum_values[$x] += $ifSpeeds{ $key }; $counter++; } else { $x++; $sum_values[$x] = $ifSpeeds{ $key }; $counter = 1; }

        To avoid the almost-duplication of the key statement, you could also write

        $sum_values[$x] += $ifSpeeds{ $key }; $x++ unless ++$counter % 8;

        or even simply

        $sum_values[$counter++/8] += $ifSpeeds{ $key }; # the [] impli +es int(...)

        (Perl does not generate a warning when you do something like $sum += ..., with $sum being undef initially — it treats it as 0)

Re: Looping Over Hash Skips an Element?
by Lotus1 (Hermit) on Jan 06, 2012 at 18:28 UTC

    I'm wondering why you need a hash. You are indexing anyway so why not just have two arrays, one for the keys and one for the values.

    Another thing I notice is that your keys contain an index. Why not use that number as the index for an array that holds the values? When you need to print you can add the Gi3 part in the print statement. An array would let you do the sum of 8 (or more) values at a time. $sum=$a[$n]+$a[$n+1]+ ... You could use a c style for loop to iterate through every 8th index.

      From them names I guesstimate he populates this table from the SNMP interface MIB. The index of the ifTable is not necessarily contiguous. Some devices give you 1,2,3.. then jump to 100 or even 1000. So that is not practical as array index. I had the same problem and solved it with hashes plus a custom sort function:
      sub sort_by_intf # sort interface alpha/number name mixture properly { my $A = $a; # just turn any number into a 0004 digit one my $B = $b; $A =~ s/([0-9]+)/ sprintf "%04d",9999-$1 /ge; $B =~ s/([0-9]+)/ sprintf "%04d",9999-$1 /ge; $B cmp $A; # reverse order happens to be useful here: } # Tun0, Ser1, Port-channel2, Gi3, Fa4, Eth5
      Then whenever you loop over the keys, just sort them with that function like so:
      for (sort sort_by_intf keys %ifSpeed) { printf "intf: %s, speed: %d\n" $_, $ifSpeed{$_}; }
Re: Looping Over Hash Skips an Element?
by Xiong (Hermit) on Jan 07, 2012 at 20:26 UTC

    If the form of the data is appropriate to a hash then order of elements doesn't matter. If order of elements matters then a hash is not an appropriate representation. Of course it is always reasonable to extract keys or values from a hash into an array and sort that; or sort arrays and assemble them into hashes.

    Therefore construct @name_this_array_wisely = keys %ifSpeeds; Sort that array to your taste and iterate through it.

    I'm not the guy you kill, I'm the guy you buy. —Michael Clayton
Re: Looping Over Hash Skips an Element?
by mmartin (Monk) on Jan 09, 2012 at 15:11 UTC
    Hey Guys,

    Thanks for the replies... Sorry it took me a few days to get back but I got pretty sick on Friday and was unable to even look at a computer screen.

    Yea I don't know why but for some reason I just instantly went to a Hash. I think it was because I had been working with them so much lately and it just kind of became a habit.
    What would be the "rule of thumb" in regards to when to use a hash or a 2D-Array instead..?

    Thanks Again,
    Matt


Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://946635]
Approved by toolic
Front-paged by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (6)
As of 2014-07-10 07:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When choosing user names for websites, I prefer to use:








    Results (202 votes), past polls