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

The following code produces a stingified hash. Can someone explain how it works? Thanks in advance.
use strict; use warnings; my %hash = ( A => 'a', B => 'b', C => 'c', D => 'd', E => 'e', F => 'f' ); print "@{[%hash]}";

Replies are listed 'Best First'.
Re: Stringified hash
by dragonchild (Archbishop) on Apr 12, 2004 at 12:16 UTC
    It works the same as
    use strict; use warnings; my %hash = ( A => 'a', B => 'b', C => 'c', D => 'd', E => 'e', F => 'f' ); my @x = %hash; print "@x\n";

    Basically, you're imposing list context on the hash, which gives you back the key-value pairs (in semi-random order). The @{[ ... ]} construct is a way of imposing list context within an interpolating string. *shrugs* Neat corner case. :-)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

Re: Stringified hash
by liz (Monsignor) on Apr 12, 2004 at 12:21 UTC
    %hash # flatten hash to key/value pairs [%hash] # create anonymous list reference to flattened hash @{[%hash]} # dereference anonymous array reference "@{[%hash]}" # interpolate in double quoted string (with $" inbet +ween)

    Hope this helps.

    Liz

Re: Stringified hash
by Roger (Parson) on Apr 12, 2004 at 12:20 UTC
    In the code @{[ %hash ]}:

    [ ... ] creates an anonymous list reference (from the given hash). And @{ ... } dereferences the anonymous list reference. And then the double quotes interpolate the elements with spaces.

      an anonymous list reference

      That's an array reference, not a list reference. Run perldoc -q "difference between" (or check out perldoc.com's version) for a discussion on the difference. I'm sorry to be such a terminology nazi, but this is quickly becoming a pet-peeve of mine. :-)

        Thanks for the correction. :-)

Re: Stringified hash
by Anonymous Monk on Apr 12, 2004 at 12:26 UTC
    Great! Thanks all! The script is actually creating a dB query something like this. The code worked but I wasn't totally sure how. Thanks again.
    use strict; use warnings; my %hash = ( A => 'a', B => 'b', C => 'c', D => 'd', E => 'e', F => 'f' ); print "@{[%hash]}"; while(my ($key, $value) = each(%hash)) { $hash{$key} = '= '.$value.' AND'; } my $query = 'SELECT row_id FROM Table WHERE '.substr("@{[%hash]}",0,-4 +); print "\n\n$query\n";
      You'll want to rewrite that code, so that it's not obfuscated.
      my @clauses; my @params; while (my ($k, $v) = each %hash) { push @clauses, "$k = ?"; push @params, $v; } my $query = "SELECT row_id FROM Table WHERE " . join(" AND ", @clauses +);

      Then, when you execute that query using DBI, you pass @params to the execute() call. This will close a few security holes you currently have, as well as a few potential bugs. And, it's easier to read. :-)

      ------
      We are the carpenters and bricklayers of the Information Age.

      Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

        This way worked out nicely for me. I guess I immediately thought of hash when an array would do. I'm not too worried about security. There is no interface, I'm processing files and loading them into a dB and I'm using ODBC. Thanks for the hand.
        use strict; use warnings; my @array = ( "A = 'a'", "B = 'b'", "C = 'c'", "D = 'd'", "E = 'e'", "F = 'f'" ); print "@array\n"; my $query = 'SELECT row_id FROM Table WHERE '.join(' AND ', @array); print "\n\n$query\n";
        See, I personally find this much easier to read, but I tend to find each loops generally hard to read. (I recognize that they're sometimes necessary for efficiency reasons, but still consider them ugly) Maybe it's just me.
        my @fields = keys(%hash); my @params = @hash{@fields}; my $query = "SELECT row_id FROM Table WHERE " . join(" AND ", map {"$_ = ?"} @fields);
        If for some reason the interface you have won't accomodate the extra separate @params parameter, you could also do this, though this depends on having the DBI handle available when you're forming the query:
        my @fields = keys(%hash); my $query = "SELECT row_id FROM Table WHERE " . join(" AND ", map {"$_ = " . $dbh->quote($hash{$_})} @fields);
        However, I'd still go with the first, assuming that passing along the @params list is doable.