http://qs321.pair.com?node_id=1220543

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

Dearest Monks,

Let's say I've got an array of hashes like this:

@AoH = ( { name => Adam, age => 0 }, { name => Bob, age => 10 }, { name => Cat, age => 20 } );
and I want to produce a 'quoted' comma separated list (a bit like CSV) of the names, i.e.:
'Adam','Bob','Cat'
can anyone offer more concise/efficient/elegant ways of doing it than this kind of approach, which does work, and is pretty concise, but ain't pretty:
$csv .= ",'$AoH[$_]{name}'" for (0..$#AoH); $csv =~ s/^,//;
Order of names doesn't matter.

Update: If there are no names (i.e. @AoH is empty), the output should be:
''
i.e. 2 single quotes, but my solution above doesn't meet that requirement (I've replied to Rolf below with some which do, though).

Thanks.
tel2

Replies are listed 'Best First'.
Re: Make CSV list from values in AoH
by kevbot (Vicar) on Aug 18, 2018 at 06:44 UTC
    Hello tel2,

    Here is a way to do it with help from the Text::CSV::Slurp module.

    #!/usr/bin/env perl use strict; use warnings; use Text::CSV::Slurp; my @aoh = ( { name => 'Adam', age => 0 }, { name => 'Bob', age => 10 }, { name => 'Cat', age => 20 } ); open my $fh, '>', 'output.csv' or die $!; if( scalar @aoh > 0 ){ my $csv = Text::CSV::Slurp->create( input => \@aoh, always_quote => 1, quote => "'" ); print $fh $csv; } else { print $fh "''"; } $fh->close; exit;
      Thanks for that, kevbot.

      Not bad for a bot!

      tel2

Re: Make CSV list from values in AoH
by kcott (Archbishop) on Aug 18, 2018 at 05:35 UTC

    G'day tel2,

    How's things on your side of the ditch; it's cold, wet and windy on my side. :-(

    I saw your exchanges with LanX. I believe the following script addresses all the issues raised in that subthread as well as the update to your OP. It:

    • only uses one map
    • only requires one change if you want a different quoting character
    • handles all FALSE, scalar values
    • handles missing keys
    • quotes in the manner specified in the OP and the update
    #!/usr/bin/env perl use strict; use warnings; my @AoH_1 = ( { name => 'Ant', age => 0 }, { name => '0', age => 10 }, { name => 'Bob', age => 20 }, { name => 0, age => 30 }, { name => 'Cat', age => 40 }, { name => '', age => 50 }, { name => 'Dog', age => 60 }, { name => undef, age => 70 }, { name => 'Eel', age => 80 }, { age => 90 }, { name => 'Fox', age => 99 }, ); my @AoH_2 = (); my $q = "'"; for (\@AoH_1, \@AoH_2) { my @AoH = @$_; print 'Elements in AoH: ', 0+@AoH, "\n"; my $csv; if (@AoH) { $csv = join ',', map { defined $AoH[$_]{name} ? "$q$AoH[$_]{name}$q" : "$q$q" } 0 .. $#AoH; } else { $csv = "$q$q"; } print "CSV: $csv\n"; }

    Output:

    Elements in AoH: 11 CSV: 'Ant','0','Bob','0','Cat','','Dog','','Eel','','Fox' Elements in AoH: 0 CSV: ''

    — Ken

      G'day Ken,

      Similar weather here, mate.

      Thanks for your comprehensive solution.

      tel2

Re: Make CSV list from values in AoH
by AnomalousMonk (Archbishop) on Aug 18, 2018 at 07:17 UTC

    Another way. Distinguishes undefined from non-existent keys (may be surplus to requirements). Needs 5.10+ for the  // defined-or operator.

    c:\@Work\Perl\monks>perl -wMstrict -le "use 5.010; ;; my @AoH = ( { name => 'Adam', age => 0, }, { name => 'Bob', age => 10, }, { name => 'Cat', age => 20, }, { name => '0', age => 30, }, { name => 0, age => 35, }, { name => '', age => 40, }, { name => undef, age => 50, }, { age => 60, }, ); ;; my @empty; ;; print make_csv('name', @AoH); print make_csv('name', @empty); print make_csv('name'); print make_csv(); print make_csv('name', { name => 'Foo', age => 91, }); print make_csv('name', { name => '0', age => 92, }); print make_csv('name', { name => 0, age => 93, }); print make_csv('name', { name => '', age => 94, }); print make_csv('name', { name => undef, age => 95, }); print make_csv('name', { age => 96, }); ;; sub make_csv { my $k = shift; return qq{'@{[ join q{','}, map exists $_->{$k} ? $_->{$k} // '[UNDEF]' +: '???', @_ ]}'}; } " 'Adam','Bob','Cat','0','0','','[UNDEF]','???' '' '' '' 'Foo' '0' '0' '' '[UNDEF]' '???'


    Give a man a fish:  <%-{-{-{-<

      Thanks AnomalousMonk,

      Your effort is appreciated even if it wouldn't work with 'strict' in use.

      tel2

        ... it wouldn't work with 'strict' in use.

        Whatever makes you think that? See command line invocation
            c:\@Work\Perl\monks>perl -wMstrict -le
        which enables full strictures (and also, I must admit, global warnings; not, in general, a good idea, but OK for command line examples). (Update: In general, everything I post on PerlMonks and all the code I write has lexical strictures and warnings fully enabled, with only very specific strictures/warnings lexically disabled. I consider this a Best Practice.)


        Give a man a fish:  <%-{-{-{-<

Re: Make CSV list from values in AoH
by LanX (Saint) on Aug 17, 2018 at 22:43 UTC
    you may want to try something like

      $csv  = join "," , map { "'$_'" }  map { $AoH[$_]{name} } 0 .. $#AoH;

    update

    maybe clearer are two lines

    push @names, $_->{name} for @AoH; $csv = join "," , map { "'$_'" } @names;

    update
    thinking about it, I'd personally prefer :)

    $csv = join "," , map { "'$_'" } map { $_->{name} } @AoH;

    of course you are free to merge the two maps into one :)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      Thanks very much, Rolf.

      All your solutions seem to work to my original spec's, thanks!

      Sorry to report that I forgot to mention that if there are no names, the output should be 2 single quotes (see update in my first post).

      I've come up with this, which is based on your 1st solution:
          $csv = "'" . (join "','", map{$_} map{$AoH[$_]{name}} 0..$#AoH) . "'";
      or more simply:
          $csv = "'" . (join "','", map{$AoH[$_]{name}} 0..$#AoH) . "'";
      or even:
          $csv = "'" . (join "','", map{$_->{name}} @AoH) . "'";

      BTW, how do I merge the 2 maps you mentioned into 1?

        Your approach is not advisable.

        Just imagine you need to change the quote from ' to ", then you'll have to change 4 code places instead of 2.

        depends what "if there are no names" means.

        • the key "name" is missing
        • the value for "name" is ""
        • the value for "name" is undef

        map{ $AoH[$_]{name} || "" } should cover all cases except if the name "0" is not allowed (untested)

        good night! :)

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Make CSV list from values in AoH
by Tux (Canon) on Aug 19, 2018 at 12:47 UTC
    1. You code is not clean and won't run under strict (please wuote the names)
    2. You did not specify what to do is a name contains quotation (or worse, newlines)

    Enjoy, Have FUN! H.Merijn