App-CSVUtils

 view release on metacpan or  search on metacpan

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

	my %fuzzy = map {$_->[0]=>1, $_->[1]=>1} @fuzzy;
	map { die [400, "Cannot use the same field for both exact and fuzzy expression matching"] if
		  $fuzzy{$_->[0]} or  $fuzzy{$_->[1]} } @lookup_fields;

        my %fill_fields; # key=fieldname-in-target, val=fieldname-in-source
        {
            my @ff = ref($r->{util_args}{fill_fields}) eq 'ARRAY' ?
                @{$r->{util_args}{fill_fields}} : split(/,/, $r->{util_args}{fill_fields});
            for my $field_idx (0..$#ff) {
                my @ff2 = split /:/, $ff[$field_idx], 2;
                if (@ff2 < 2) {
                    $ff2[1] = $ff2[0];
                }
                $fill_fields{ $ff2[0] } = $ff2[1];
            }
        }

        # these are the keys that we add to the stash
        $r->{lookup_fields} = \@lookup_fields;
	$r->{fuzzy} = \@fuzzy;
        $r->{fill_fields} = \%fill_fields;
        $r->{source_fields_idx} = [];
        $r->{source_fields} = [];
        $r->{source_data_rows} = [];
        $r->{target_fields_idx} = [];
        $r->{target_fields} = [];
        $r->{target_data_rows} = [];
    },

    on_input_header_row => sub {
        my $r = shift;

	#TARGET
        if ($r->{input_filenum} == 1) {
	    #JDP: Optionally append fuzzy matched target fields
	    if( $r->{util_args}{regex_fill} ){
		$r->{fill_fields}->{ join '.', @{$_} }=$_->[1] foreach
		    @{$r->{fuzzy}};
	    }

	    #JDP: lookup-fields has undocumented expectation of headers for
	    #     empty target columns. This provides more DWIM behavior by
	    #     patching in implicit headers a la csv-add-fields
	    my $target_count = @{ $r->{input_fields} };
	    my %target_fields = map {$_=>1} @{ $r->{input_fields} };
	    foreach my $field ( keys %{ $r->{fill_fields} } ){
		unless( exists($target_fields{$field}) ){
		    push @{ $r->{input_fields} }, $field;
		    $r->{input_fields_idx}->{$field}=$target_count++;
		}
	    }

	    $r->{target_fields}     = $r->{input_fields};
	    $r->{target_fields_idx} = $r->{input_fields_idx};
	    $r->{output_fields}     = $r->{input_fields};

	    #JDP: Check join field names exist
	    #XXX Case-insensitivity?
	    foreach( @{$r->{lookup_fields}}, @{$r->{fuzzy}} ){
		my $out = $_->[0];
		die [404, "Unknown target field: $out"] unless
		    $r->{input_filenum}==1 && exists $r->{target_fields_idx}->{$out};
	    }
	    foreach my $k ( keys %{$r->{fill_fields}} ){
		die [404, "Unknown target fill field: $k"] unless
		    $r->{input_filenum}==1 && exists $r->{target_fields_idx}->{$k};
	    }

	#SOURCE
	} else {
            $r->{source_fields}     = $r->{input_fields};
            $r->{source_fields_idx} = $r->{input_fields_idx};

	    #JDP: Check join field names exist
	    #XXX Case-insensitivity?
	    foreach( @{$r->{lookup_fields}}, @{$r->{fuzzy}} ){
		my $src = $_->[1];
		die [404, "Unknown source field: $src"] unless
		$r->{input_filenum}==2 && exists $r->{source_fields_idx}->{$src};
	    }
	    foreach my $v ( values %{$r->{fill_fields}} ){
		die [404, "Unknown source fill field: $v"] unless
		    $r->{input_filenum}!=1 && exists $r->{source_fields_idx}->{$v};
	    }

	}

    },

    on_input_data_row => sub {
        my $r = shift;
        if ($r->{input_filenum} == 1) {
            push @{ $r->{target_data_rows} }, $r->{input_row};
        } else {
            push @{ $r->{source_data_rows} }, $r->{input_row};
        }
    },


    after_close_input_files => sub {
        my $r = shift;

        my $ci = $r->{util_args}{ignore_case};
        #my $fuzzy = exists($r->{util_args}{regex}) ? 1 : 0;
        my $fuzzy = scalar @{ $r->{fuzzy} };

        #Prep key separator. Original use of | is a bad option for fuzzy regex mode
        my $keySepIN = $r->{util_args}{key_record_separator};
        my $keySep = eval{chr("0$1")} if defined($keySepIN) && $keySepIN =~ /^0(x\{?[0-9a-fA-F]+\}?|[0-9+]{2})$/;
        $keySep = "\000" if $@;
        $keySep //= "\000";

        my @inner;
        my $inner = $r->{util_args}{inner};
        eval 'use Storable' if $inner;
        if( $@ ){
            warn "Cannot load Storable, unable to fulfill --inner: $@\n";
            $inner = 0;
        }

        # 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';
            }



( run in 0.533 second using v1.01-cache-2.11-cpan-788537b7465 )