$VAR1 = { '010' => { 'Gross' => '15', 'name' => 'Moe', 'widget-2' => { 'Gross' => '15', 'Qty' => '1' }, 'qty' => '1' }, '020' => { 'Gross' => '35', 'name' => 'Larry', 'widget-1' => { 'Gross' => '15', 'Qty' => '1' }, 'widget-2' => { 'Gross' => '20', 'Qty' => '2' }, 'qty' => '3' } }; #### # perl # http://www.perlmonks.org/?node_id=859450 use strict; use warnings; use diagnostics; use 5.010; use Data::Dumper; my %input; my $line = ; # skip 1st line while ( my $line = ) { chomp $line; my ($tx, $name, $type, $product, $qty, $gross) = split /\s*,\s*/, $line; #say "$tx, $name, $type, $product, $qty, $gross"; if ( $type eq 'Cart') { #say 'process Cart line'; #say "data is $tx, $name, $qty, $gross"; $input{$tx} = { 'name' => $name, 'qty' => $qty, 'Gross'=> $gross, } } elsif ( $type eq 'Item' ) { #say 'process Item line'; #say "data is $tx, $name, $qty, $gross"; $input{$tx} = { %{$input{$tx}}, $product => { 'Qty' => $qty, 'Gross' => $gross, }, } } else { warn 'line not recognized'; } } #say Dumper(\%input); my @items = qw( widget-1 widget-2); # all available items, need to be defined in advance to have a stable order of output items say 'Txn ID, Name , Qty, Gross, widget-1 Qty, widget-1 Gross, widget-2 Qty, widget-2 Gross'; foreach my $tx (keys %input) { #say "prcess transaction $tx"; my $lineout = "$tx, $input{$tx}{'name'}, $input{$tx}{'qty'},$input{$tx}{'Gross'}, "; foreach my $item ( @items ) { #say "found key $key"; #say "found data $input{$tx}{$key}"; #say "process item $input{$tx}{$item}"; $lineout .= ", "; $lineout .= $input{$tx}{$item}{'Qty'} // ''; $lineout .= ", "; $lineout .= $input{$tx}{$item}{'Gross'} // ''; } say $lineout; } __DATA__ Txn ID, Name , Type , Product ID, Qty, Gross 010 , Moe , Cart , , 1 , 15 010 , Moe , Item , widget-2 , 1 , 15 020 , Larry, Cart , , 3 , 35 020 , Larry, Item , widget-1 , 1 , 15 020 , Larry, Item , widget-2 , 2 , 20 #### Txn ID, Name , Qty, Gross, widget-1 Qty, widget-1 Gross, widget-2 Qty, widget-2 Gross 010, Moe, 1,15, , , , 1, 15 020, Larry, 3,35, , 1, 15, 2, 20