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

Hello Monks,

I was playing around with ARRAYS OF HASHES in order to reduce the defined scalars on my script. I manage to get to the point that I want with a working solution but I am wondering if I can do the same work with less lines of code.

I am using an Array of Hashes to pass the values that I want. Then I am trying to extract the values into an array. I will apply the reversed process afterwards (values map to an Array of Hashes).

For the moment I am using two for loops to extract the values from an Array of Hashes, but I was wondering if I can use map to do the same thing in one line instead of having two loops.

Sample of code and output:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Time::HiRes qw( gettimeofday ); my @Array_Hash_To_Send = ( { LI_VN_Mode => '00100011' }, # 8 bit { Stratum => '0' }, # 8 bit { Poll => '0' }, # 8 bit { Precision => '0' }, # 8 bit { Root_Delay => '0' }, # 32 bit { Dispersion => '0' }, # 32 bit { Reference_Identifier => '0' }, # 32 bit { Reference_Timestamp_Sec => '0' }, # 32 bit { Reference_Timestamp_Micro_Sec => '0' }, # 32 bit { Originate_Timestamp_Sec => '0' }, # 32 bit { Originate_Timestamp_Micro_Sec => '0' }, # 32 bit { Receive_Timestamp_Sec => '0' }, # 32 bit { Receive_Timestamp_Micro_Sec => '0' }, # 32 bit { Transmit_Timestamp_Sec => '0' }, # 32 bit { Transmit_Timestamp_Micro_Sec => '0' }, # 32 bit ); ($Array_Hash_To_Send[13]{Transmit_Timestamp_Sec} , $Array_Hash_To_Send +[14]{Transmit_Timestamp_Micro_Sec}) = gettimeofday(); # For demonstration purposes only my @arraySendSntpPacket = ( $Array_Hash_To_Send[0]{LI_VN_Mode}, $Array_Hash_To_Send[1]{Stratum}, $Array_Hash_To_Send[2]{Poll}, $Array_Hash_To_Send[3]{Precision}, $Array_Hash_To_Send[4]{Root_Delay}, $Array_Hash_To_Send[5]{Dispersion}, $Array_Hash_To_Send[6]{Reference_Identifier}, $Array_Hash_To_Send[7]{Reference_Timestamp_Sec}, $Array_Hash_To_Send[8]{Reference_Timestamp_Micro_Sec}, $Array_Hash_To_Send[9]{Originate_Timestamp_Sec}, $Array_Hash_To_Send[10]{Originate_Timestamp_Micro_Sec} +, $Array_Hash_To_Send[11]{Receive_Timestamp_Sec}, $Array_Hash_To_Send[12]{Receive_Timestamp_Micro_Sec}, $Array_Hash_To_Send[13]{Transmit_Timestamp_Sec}, $Array_Hash_To_Send[14]{Transmit_Timestamp_Micro_Sec} +); my @Test_Array; foreach my $href ( @Array_Hash_To_Send ) { foreach my $role ( keys %$href ) { push @Test_Array, $href->{$role}; } } my @Test_Array_Values = map { } keys print Dumper \@arraySendSntpPacket; print Dumper \@Test_Array; my @Test_Array_Values = values @Array_Hash_To_Send; __END__ $VAR1 = [ '00100011', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', 1437578739, 958207 ]; $VAR1 = [ '00100011', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', 1437578739, 958207 ];

Thank you in advance for your time and effort reading and replying to my question.

Seeking for Perl wisdom...on the process of learning...not there...yet!

Replies are listed 'Best First'.
Re: How to extract the values of an Array of Hashes
by shmem (Chancellor) on Jul 22, 2015 at 15:54 UTC
    my @Test_Array_Values = map { } keys

    not quite. This:

    my @Test_Array_Values = map { values %$_ } @Array_Hash_To_Send;

    But each hash in your AoH contains only one key/value pair, and the keys are unique. So, what's the point to have them stuffed in an array? - I guess it is the order of parameters. But then, why are you using hashes, when you are interested in the values only, and not passing named parameters?

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

      Hello shmem,

      Exactly as you said it, I am interested in the values only. But for maintenance and readability reasons I am using AoH.

      I was reading about Named Arguments which describes exactly what I need. But the reason that I am using AoH is save resources by defining less scalars than before. But if I use the Named Arguments solution, I will end up where I started. Unless I am missing something.

      Thank you for your time and effort reading and providing a solution to my problem.

      Seeking for Perl wisdom...on the process of learning...not there...yet!
        But if I use the Named Arguments solution, I will end up where I started. Unless I am missing something.

        You are missing something. You can use a hash to store key/value pairs instead of having many scalar variables. As you probably know, the order of hash keys retrieved by keys is arbitrary. But you can also have an array of keys, which keeps them ordered.

        Using named parameters doesn't re-introduce the scalar values: you just pass the hash to the subroutine. Inside the subroutine, you can assign the passed parameters (which is a flat list of key/value pairs) to a hash and retrieve the values with the original keys.

        my %Hash_To_Send = ( LI_VN_Mode => '00100011' , # 8 bit Stratum => '0' , # 8 bit Poll => '0' , # 8 bit Precision => '0' , # 8 bit Root_Delay => '0' , # 32 bit Dispersion => '0' , # 32 bit Reference_Identifier => '0' , # 32 bit Reference_Timestamp_Sec => '0' , # 32 bit Reference_Timestamp_Micro_Sec => '0' , # 32 bit Originate_Timestamp_Sec => '0' , # 32 bit Originate_Timestamp_Micro_Sec => '0' , # 32 bit Receive_Timestamp_Sec => '0' , # 32 bit Receive_Timestamp_Micro_Sec => '0' , # 32 bit Transmit_Timestamp_Sec => '0' , # 32 bit Transmit_Timestamp_Micro_Sec => '0' , # 32 bit ); my @keys = qw( LI_VN_Mode Stratum Poll Precision Root_Delay Dispersion Reference_Identifier Reference_Timestamp_Sec Reference_Timestamp_Micro_Sec Originate_Timestamp_Sec Originate_Timestamp_Micro_Sec Receive_Timestamp_Sec Receive_Timestamp_Micro_Sec Transmit_Timestamp_Sec Transmit_Timestamp_Micro_Sec ); # get values in ordered mode for call with positional parameters my @arraySendSntpPacket = @Hash_To_Send{@keys}; # hash slice! my $result; # two ways of calling: # positional parameters $result = positional_function( @arraySendSntpPacket ); # named parameters $result = named_param_function( %Hash_To_Send ); sub positional_function { my @args = @_; # do something with $args[0], $args[1], ... } sub named_param_function { my %args = @_; # flattened list of key/value pairs # do something with $args{LI_VN_Mode}, $args{Stratum}, ... }
        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: How to extract the values of an Array of Hashes
by jeffa (Bishop) on Jul 22, 2015 at 15:46 UTC

    You mean like this?

    my @To_Send = ( { LI_VN_Mode => '00100011' }, # 8 bit { Stratum => '0' }, # 8 bit { Poll => '0' }, # 8 bit { Precision => '0' }, # 8 bit { Root_Delay => '0' }, # 32 bit { Dispersion => '0' }, # 32 bit { Reference_Identifier => '0' }, # 32 bit { Reference_Timestamp_Sec => '0' }, # 32 bit { Reference_Timestamp_Micro_Sec => '0' }, # 32 bit { Originate_Timestamp_Sec => '0' }, # 32 bit { Originate_Timestamp_Micro_Sec => '0' }, # 32 bit { Receive_Timestamp_Sec => '0' }, # 32 bit { Receive_Timestamp_Micro_Sec => '0' }, # 32 bit { Transmit_Timestamp_Sec => '0' }, # 32 bit { Transmit_Timestamp_Micro_Sec => '0' }, # 32 bit ); my @SendSntpPacket = map values %$_, @To_Send; print Dumper \@SendSntpPacket;
    I took the liberty of renaming your variables because i see no reason to label them as such. They are what they are. :)

    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)
    

      Hello jeffa,

      Exactly what I needed, I am so stupid. I should have thought on Postfix Dereference Syntax dereferencing the array and then simply extracting the values.

      Thank you for your time and effort.

      Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: How to extract the values of an Array of Hashes
by 1nickt (Canon) on Jul 22, 2015 at 17:04 UTC

    This seems convoluted.

    Why not:

    #! perl use strict; use warnings; use feature qw/ say /; use Carp qw/ croak confess /; use Data::Dumper; use Time::HiRes qw( gettimeofday ); my %To_Send = ( '010_LI_VN_Mode' => '00100011', # 8 bit '020_Stratum' => '0', # 8 bit '030_Poll' => '0', # 8 bit '040_Precision' => '0', # 8 bit '050_Root_Delay' => '0', # 32 bit '060_Dispersion' => '0', # 32 bit '070_Reference_Identifier' => '0', # 32 bit '090_Reference_Timestamp_Sec' => '0', # 32 bit '100_Reference_Timestamp_Micro_Sec' => '0', # 32 bit '110_Originate_Timestamp_Sec' => '0', # 32 bit '120_Originate_Timestamp_Micro_Sec' => '0', # 32 bit '130_Receive_Timestamp_Sec' => '0', # 32 bit '140_Receive_Timestamp_Micro_Sec' => '0', # 32 bit '150_Transmit_Timestamp_Sec' => '0', # 32 bit '160_Transmit_Timestamp_Micro_Sec' => '0', # 32 bit ); ($To_Send{'150_Transmit_Timestamp_Sec'}, $To_Send{'160_Transmit_Timestamp_Micro_Sec'}) = gettimeofday(); my @Packet; push (@Packet, $To_Send{ $_ }) for (sort { $a cmp $b } keys %To_Send); say Dumper \@Packet; __END__

    Output:

    $VAR1 = [ '00100011', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', 1437584624, 585058 ];
    The way forward always starts with a minimal test.

      I would much rather depend on Tie::IxHash than having to prepend my keys with numbers to preserve order. This could lead to a less than desirable maintenance issue down the road.

      use strict; use warnings; use Data::Dumper; use Tie::IxHash; tie(my %To_Send, 'Tie::IxHash', LI_VN_Mode => '00100011', # 8 bit Stratum => '0', # 8 bit Poll => '0', # 8 bit Precision => '0', # 8 bit Root_Delay => '0', # 32 bit Dispersion => '0', # 32 bit Reference_Identifier => '0', # 32 bit Reference_Timestamp_Sec => '0', # 32 bit Reference_Timestamp_Micro_Sec => '0', # 32 bit Originate_Timestamp_Sec => '0', # 32 bit Originate_Timestamp_Micro_Sec => '0', # 32 bit Receive_Timestamp_Sec => '0', # 32 bit Receive_Timestamp_Micro_Sec => '0', # 32 bit Transmit_Timestamp_Sec => '0', # 32 bit Transmit_Timestamp_Micro_Sec => '0', # 32 bit ); print Dumper \%To_Send;

      UPDATE: I never said anything about scalability. I stated my preferences. You can labor away at such code all you want to. How you derive "decrease dependencies" from "reduce defined scalars" makes zero sense as well.

      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)
      

        Hello again jeffa,

        Nice alternative solution, using this module. I will check it and give a try. Thanks again for the time and effort.

        Seeking for Perl wisdom...on the process of learning...not there...yet!

        That works too, of course, and jeffa is right that trying to manually order hash elements is not very scaleable. But the OP said he wanted to "reduce the defined scalars" in his script, so I would think that he would prefer to not increase the number of dependencies, also. He is passing the hash to a sub whose parameters are known and stable, and the numbering scheme leaves room for easy maintenance and expansion. In this case numbering the hash elements is fine IMO.

        The way forward always starts with a minimal test.

      Hello 1nickt,

      Interesting idea of modifying the key with an numeric value, so I could use it to sort the hash in ascending order. Nice trick, I will keep that in mind for future implementation.

      Thank you for your time and effort reading and replying to my question.

      Seeking for Perl wisdom...on the process of learning...not there...yet!