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
Re: Make CSV list from values in AoH
by kevbot (Vicar) on Aug 18, 2018 at 06:44 UTC
|
#!/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;
| [reply] [d/l] |
|
Thanks for that, kevbot.
Not bad for a bot!
tel2
| [reply] |
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: ''
| [reply] [d/l] [select] |
|
| [reply] |
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: <%-{-{-{-<
| [reply] [d/l] [select] |
|
| [reply] |
|
... 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: <%-{-{-{-<
| [reply] [d/l] [select] |
|
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 :)
| [reply] [d/l] [select] |
|
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?
| [reply] [d/l] [select] |
|
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! :)
| [reply] [d/l] |
|
Re: Make CSV list from values in AoH
by Tux (Canon) on Aug 19, 2018 at 12:47 UTC
|
| [reply] |
|
|