Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

condense conditional

by mdidomenico (Initiate)
on Sep 12, 2018 at 13:02 UTC ( [id://1222210]=perlquestion: print w/replies, xml ) Need Help??

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

#!/usr/bin/perl use strict; use warnings; my $devstats; readfile("/sys/class/net/p2p1/statistics/rx_bytes"); exit(0); sub readfile { my $file = shift; my @data = split("/", $file); my $nic = $data[4]; my $key = $data[-1]; my $dirCnt = $#data; open(FIFO, "< $file"); my $value = <FIFO>; if (defined($value)) { chomp($value); if ($dirCnt eq 6) { $devstats->{$nic}->{$data[5]}->{$key} = $value; } elseif ($dircnt eq 7) { $devstats->{$nic}->{$data[5]}->{$data[6]}->{$key} = $value; } elseif ($dircnt eq 8) { $devstats->{$nic}->{$data[5]}->{$data[6]}->{$data[7]}->{$key} = +$value; } elseif ($dircnt eq 9) { $devstats->{$nic}->{$data[5]}->{$data[6]}->{$data[7]}->{$data[8] +}->{$key} = $value; } else { $devstats->{$nic}->{$key} = $value; } } close(FIFO); }
How can i condense the if conditional, from the above code snippet, into a smaller block and one that handles variable lengths? I thought about using a for loop, but I'm not able to conjure anything that tells me how to append keys to a hash.

Replies are listed 'Best First'.
Re: condense conditional
by Corion (Patriarch) on Sep 12, 2018 at 13:18 UTC

    Instead of using a conditional, you could just store things in iteratively deeper hashes until you run out of elements in @data:

    sub store_data { my( $root, $value, @key ) = @_; while( @key > 1 ) { $k = shift @key; $root->{ $k } ||= {}; $root = $root->{ $k }; }; $root->{ $key[0] } = $value; } store_data( $devstats, $value, @data );

    Update: Code was missing a line

      How does this work, given that you are never changing @key and you never set $k? And doesn't $root = $root->{ $k }; just dereference rather than move the root?

      Update: to answer my own questions - the previously missing line allows for the changes and the root moves because of the explicit creation of the sub-hashref. Good solution (++).

Re: condense conditional
by tybalt89 (Monsignor) on Sep 12, 2018 at 14:35 UTC

    Semi-tested :)

    #!/usr/bin/perl # https://perlmonks.org/?node_id=1222210 use strict; use warnings; my $devstats; readfile("/sys/class/net/dsl/statistics/rx_bytes"); readfile("/sys/class/net/dsl/mtu"); readfile("/sys/class/net/tybalt/mtu"); readfile("/sys/class/net/tybalt/statistics/rx_bytes"); readfile("/sys/class/net/dsl/queues/tx-0/byte_queue_limits/inflight"); use Data::Dumper; print Dumper $devstats; exit(0); sub readfile { my $file = shift; my @data = split("/", $file); my $nic = $data[4]; open(FIFO, "< $file") or die "$! opening $file"; my $value = <FIFO>; if (defined($value)) { chomp($value); my $hashref = \$devstats->{$nic}; $hashref = \$$hashref->{$_} for @data[5 .. $#data]; $$hashref = $value; } close(FIFO); }

    Outputs (on my system):

    $VAR1 = { 'tybalt' => { 'statistics' => { 'rx_bytes' => '26296' }, 'mtu' => '1500' }, 'dsl' => { 'mtu' => '1500', 'queues' => { 'tx-0' => { 'byte_queue_limits' => +{ + 'inflight' => '0' +} } }, 'statistics' => { 'rx_bytes' => '51353977825' } } };
      my $hashref = \$devstats->{$nic}; $hashref = \$$hashref->{$_} for @data[5..$#data]; $$hashref = $value;
      should be
      my $scalarref = \$devstats->{$nic}; $scalarref = \$$scalarref->{$_} for @data[5..$#data]; $$scalarref = $value;
      This can be simplified to
      my $scalarref = \$devstats; $scalarref = \$$scalarref->{$_} for $nic, @data[5..$#data]; $$scalarref = $value;
      or
      my $scalarref = \$devstats; $scalarref = \$$scalarref->{$_} for @data[4..$#data]; $$scalarref = $value;
      This can be replaced with
      sub dive_val :lvalue { my $p = \shift; $p = \$$p->{$_} for @_; $p } dive_val($devstats, @data[4..$#data]) = $value;
      or
      use Data::Diver qw( DiveVal ); DiveVal($devstats //= {}, map \$_, @data[4..$#data]) = $value;

      How these work is explained here.

      This one works for me. Thanks!
Re: condense conditional
by fullermd (Priest) on Sep 12, 2018 at 13:14 UTC

    The most direct translation would be to just use a temporary value to store how far down the tree you are. Something like (untested, but perl -c likes it):

    if(defined($value)) { chomp($value); my $lval = $devstats->{$nic}; for(my $i = $dirCnt ; $i >= 6 ; $i--) { $lval = $lval->{$data{$i-1}}; } $lval->{$key} = $value; }

    Of course, that's a lot of magic numbers, so a better solution is probably to rearrange things to be a little more semantically meaningful. Perhaps in how the filename is assembled, and how you're passing it to readfile(), if that's possible? If not, preprocessing @data a little... it'll be longer, but probably clearer.

Re: condense conditional
by ikegami (Patriarch) on Sep 12, 2018 at 15:36 UTC

    By the way, eq is for comparing strings, and == is for comparing numbers.

Re: condense conditional
by holli (Abbot) on Sep 12, 2018 at 18:23 UTC
    Names are important. Inappropriate, misleading naming confuses people, possibly including yourself in future time. So please don't call filehandles FIFO.


    holli

    You can lead your users to water, but alas, you cannot drown them.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (7)
As of 2024-04-25 15:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found