App-CSVUtils

 view release on metacpan or  search on metacpan

lib-not_ready/App/csv_join.pm  view on Meta::CPAN


        # build lookup table w/ C-style loop for efficiency on large files
        my %lookup_table; # key = joined lookup fields, val = source row idx
        for(my $row_idx=0; $row_idx<=$#{$r->{source_data_rows}}; $row_idx++) {
            my($row, $key1, $key2);
            $row = $r->{source_data_rows}[$row_idx];
            $key1 = join $keySep, map {
                my $field = $r->{lookup_fields}[$_][1];
                my $field_idx = $r->{source_fields_idx}->{$field};
                my $val = defined $field_idx ? $row->[$field_idx] : "";
                $val = lc $val if $ci;
                $val;
            } 0..$#{ $r->{lookup_fields} };
            if( $fuzzy ){
                $key2 = join $keySep, map {
                    my $field = $r->{fuzzy}[$_][1];
                    my $field_idx = $r->{source_fields_idx}{$field};
                    my $val = defined $field_idx ? $row->[$field_idx] : '';
                    $val = lc $val if $ci;
                } 0..$#{ $r->{fuzzy} };
            } else {
                $key2 = 'STATIC';
            }
            #JDP: Split key greatly improves fuzzy match performance by binning data,
            #     thereby reducing pool of values to check with any given regexp
            $lookup_table{$key1}->{$key2} //= $row_idx;
            #warn "Prepped key1($key1)\tkey2($key2) for $row_idx\n"# unless $row_idx %20;
        }
        #use DD; dd { lookup_fields=>$r->{lookup_fields}, fill_fields=>$r->{fill_fields}, lookup_table=>\%lookup_table };

        # fill target csv
        my $rows_filled = 0;

        for(my $i=0; $i<=$#{ $r->{target_data_rows} }; $i++){
            my $row = $r->{target_data_rows}->[$i];
            my($key1, $key2);

            $key1 = join $keySep, map {
                my $field = $r->{lookup_fields}[$_][0];
                my $field_idx = $r->{target_fields_idx}{$field};
                my $val = defined $field_idx ? $row->[$field_idx] : "";
                $val = lc $val if $ci;
                $val;
            } 0..$#{ $r->{lookup_fields} };
            if( $fuzzy ){
                $key2 = join '.*?'.$keySep, map {
                    my $field = $r->{fuzzy}[$_][0];
                    my $field_idx = $r->{target_fields_idx}{$field};
                    my $val = defined $field_idx ? $row->[$field_idx] : '';
                    $val = lc $val if $ci;
                    #JDP: Wrapping is superfluous if single fuzzy key,
                    #     as is explicit match anything at beginning and ending of key
                    #     post-wrap is handled in join to reduce testing of $_
                    my $prewrap  = $_==0 ? '' : '.*?';
                    $fuzzy >1 ? $prewrap . quotemeta($val) : quotemeta($val);
                } 0..$#{ $r->{fuzzy} };
            } else {
                $key2 = 'STATIC';
            }

            #say "D:looking up '$key1'\t'$key2' ...";
            my(@row_idx, $K1LUT);
            #JDP: explore MCE for performance boost?
            if( defined($K1LUT = $lookup_table{$key1}) ){
                #warn "Matched $key1\n";
                unless( $fuzzy ){
                    @row_idx = ($K1LUT->{STATIC}) }
                else{
                    $key2 = qr/$key2/;
                    foreach my $TK2 ( keys %{$K1LUT} ){
                        push(@row_idx, $K1LUT->{$TK2}) if $TK2 =~ /$key2/;
                        #warn "$key1: Testing $TK2 =~ /$key2/ (@{[ $TK2 =~ /$key2/ ]})\t$K1LUT->{$TK2}\n" if $key1 == 734;

                        #JDP: Short-circuit unless inner join requested
                        last if scalar @row_idx && !$inner;
                    }
                }

                #say "  D:found";
                for(my $j=0; $j<=$#row_idx; $j++ ){
                    my $row = $row;
                    my $fields_filled;
                    my $row_idx = $row_idx[$j];
                    my $source_row = $r->{source_data_rows}[$row_idx];

                    $row = Storable::dclone($r->{target_data_rows}->[$i]) if $fuzzy && $j && $inner;

                    for my $field (keys %{$r->{fill_fields}}) {
                        my $target_field_idx = $r->{target_fields_idx}{$field};
                        #JDP: Why is this being checked every time? $r->{target_fields_idx} does not change.
                        #     There isn't even a clear way for its values to be undef
                        #next unless defined $target_field_idx;

                        my $source_field_idx = $r->{source_fields_idx}{ $r->{fill_fields}{$field} };
                        #JDP: Why is this being checked every time? $r->{source_fields_idx} does not change
                        #     There isn't even a clear way for its values to be undef
                        #next unless defined $source_field_idx;

                        $row->[$target_field_idx] = $source_row->[$source_field_idx];
                        $fields_filled++;
                    }

                    push @inner, $row if $fuzzy && $j && $inner;
                    $rows_filled++ if $fields_filled;
                }
            }

            #XXX: would be VERY nice to print as we go rather than spool everything, esp. for large files
            unless ($r->{util_args}{count}) {
                $r->{code_print_row}->($row);
            }
        } # for target data row


        #JDP: Inner fill, append multi-matched fuzzy source rows
        if( $inner ){
            foreach my $row ( @inner ){
                $r->{code_print_row}->($row);
            }
        }

        if ($r->{util_args}{count}) {
            $r->{result} = [200, "OK", $rows_filled];
        }
    }
);

1;
# ABSTRACT:



( run in 1.815 second using v1.01-cache-2.11-cpan-9169edd2b0e )