use Text::CSV_XS qw( csv ); my @records = ( { AB1 => 100, NN => 200, XYZ => 400 }, { AB1 => 200, XYZ => 400, MM => 300 }, { AB1 => 300, XYZ => undef, MM => "" }, ); my %seen; my @fld = sort grep { !$seen{$_}++ } map { keys %$_ } @records; csv (in => \@records, headers => \@fld, quote_empty => 1, on_in => sub { foreach my $f (@fld) { exists $_{$f} or $_{$f} = "--missing--"; } });