Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Add value from one array element into another of same ID

by Anonymous Monk
on May 27, 2021 at 15:11 UTC ( [id://11133140]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks!

I am trying to check is a value is not empty, in this case value in the 'Name" key, in one of the elements of this array and if it is, take the value of "Name" from another array where the ID matches and add that value to the empty one.
Here is a sample of the data:
... my @test = (); foreach my $data ( @{ $test_data } ) { warn Dumper $row_data; push @test, [ $data->{ ID }, $data->{ Name }, $data->{ Number }, $data->{ CODE }, ] if $data->{ Name } = ''; } ...


The data dump result:

$VAR1 = { 'ID' => '1212', 'Name' => 'JOE', 'Number' => 'XY1', 'CODE' => '6', }; $VAR1 = { 'ID' => '1212', 'Name' => '', 'Number' => 'XY1', 'CODE' => '10', }; $VAR1 = { 'ID' => '4456', 'Name' => 'MARIA', 'Number' => 'TYX', 'CODE' => '6', }; $VAR1 = { 'ID' => '4456', 'Name' => '', 'Number' => 'TYX', 'CODE' => '10', }; $VAR1 = { 'ID' => '8765', 'Name' => 'JEAN', 'Number' => 'HPO', 'CODE' => '6', };



Here is the result I am trying to get:
$VAR1 = { 'ID' => '1212', 'Name' => 'JOE', 'Number' => 'XY1', 'CODE' => '10', }; $VAR1 = { 'ID' => '4456', 'Name' => 'MARIA', 'Number' => 'TYX', 'CODE' => '10', }; $VAR1 = { 'ID' => '8765', 'Name' => 'JEAN', 'Number' => 'HPO', 'CODE' => '6', };

Thanks for looking!

Replies are listed 'Best First'.
Re: Add value from one array element into another of same ID
by pryrt (Abbot) on May 27, 2021 at 15:32 UTC
    You have ... if $data->{ Name } = '';. This will always end up assigning $data->{Name} to the empty string (because = is an assignment operator not an equality operator); and since the empty string is false, the expression evaluates to false, so the if sees false, so the push will never happen.

    String comparison is done with the eq operator, so you probably want ... if $data->{ Name } eq '';


    edit: rereading your question, that might not be all of what you need; I'm not sure I understand it fully. However, the assignment operator does need to be changed to the string equality operator to make that if clause make sense.
      It was a typo, it should've been if $data->{ Name } eq ''; to show the example. But I am still in doubt in how I could do to have the data the way I posted in my question.
Re: Add value from one array element into another of same ID
by choroba (Cardinal) on May 27, 2021 at 16:46 UTC
    You seem to need to first index the names by the ids for later retrieval. After populating the new array, you also want to add items that weren't used for replacement (JEAN here). So, we need to remember which ones were used.
    #!/usr/bin/perl use warnings; use strict; my @data = map { my %h; @h{qw{ ID Name Number CODE }} = @$_; \%h } ['1212', 'JOE', 'XY1', '6'], ['1212', '', 'XY1', '10'], ['4456', 'MARIA', 'TYX', '6'], ['4456', '', 'TYX', '10'], ['8765', 'JEAN', 'HPO', '6']; my %name; for my $d (@data) { $name{ $d->{ID} } = $d->{Name} if length $d->{Name}; } my @new; my %used; for my $d (grep ! length $_->{Name}, @data) { push @new, { %$d, Name => $name{ $d->{ID} } }; undef $used{ $name{ $d->{ID} } }; } for my $d (@data) { push @new, $d if length $d->{Name} && ! exists $used{ $d->{Name} } +; } use Data::Dumper; print Dumper \@new;
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      This worked for me, just trying to get rid of the warnings at these line:

      #Use of uninitialized value in length... for my $d (grep ! length $_->{Name}, @data) { ...
      and here:
      #Use of uninitialized value in length... push @new, $d if length $d->{Name} && ! exists $used{ $d->{Name} };
        What version of Perl are you running? length undef should return undef without warnings since 5.012 (April 2010), if I remember correctly.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Add value from one array element into another of same ID
by kcott (Archbishop) on May 28, 2021 at 08:17 UTC

    That's a poorly presented post that doesn't help us to help you:

    • It's incomplete: we can't run it!
    • The output you show could not possibly come from the code you show.
      • All you'd get would be exactly the same Dumper output (for the undeclared and unchanging $row_data) repeated the same number of times as there are elements in @{ $test_data }.
    • The (wanted) output you present does not align at all with your stated objectives.
    • There are other problems that can be seen (e.g. = instead of eq).
    • There may well be other problems that can't be seen because you haven't posted all of the code.

    Please read "How do I post a question effectively?" and "Short, Self-Contained, Correct Example" before posting again.

    Based on your prosaic description, I might have written code something like the following. Perhaps you can adapt that to your needs.

    #!/usr/bin/env perl use strict; use warnings; use Data::Dump; my @in_data = ( { ID => '1212', Name => 'JOE', REF => 1 }, { ID => '1212', Name => '', REF => 2 }, { ID => '4456', Name => 'MARIA', REF => 3 }, ); print "IN DATA:\n"; dd \@in_data; my %name_for_id; for (@in_data) { next if $_->{Name} eq ''; $name_for_id{$_->{ID}} = $_->{Name}; } my @out_data; for (@in_data) { $_->{Name} = $name_for_id{$_->{ID}} if $_->{Name} eq ''; push @out_data, [@{$_}{qw{ID Name REF}}]; } print "OUT DATA:\n"; dd \@out_data;

    Output:

    IN DATA: [ { ID => 1212, Name => "JOE", REF => 1 }, { ID => 1212, Name => "", REF => 2 }, { ID => 4456, Name => "MARIA", REF => 3 }, ] OUT DATA: [[1212, "JOE", 1], [1212, "JOE", 2], [4456, "MARIA", 3]]

    — Ken

Re: Add value from one array element into another of same ID
by BillKSmith (Monsignor) on May 27, 2021 at 17:27 UTC
    You need nested loops. The outer loop selects the data that you want to keep. The inner loop updates the CODE if a matching ID is found.
    use strict; use warnings; use Data::Dumper; my $test_data = [ {'ID' => '1212', 'Name' => 'JOE' , 'Number' => 'XY1', 'CODE' => +'6',}, {'ID' => '1212', 'Name' => '' , 'Number' => 'XY1', 'CODE' => ' +10',}, {'ID' => '4456', 'Name' => 'MARIA', 'Number' => 'TYX', 'CODE' => +'6',}, {'ID' => '4456', 'Name' => '' , 'Number' => 'TYX', 'CODE' => ' +10',}, {'ID' => '8765', 'Name' => 'JEAN' , 'Number' => 'HPO', 'CODE' => +'6',}, ]; my @result; foreach my $data ( @{ $test_data } ) { next if !$data->{Name}; my $id = $data->{ID}; push @result, $data; foreach my $temp ( @{ $test_data } ) { if (!$temp->{Name} and ($temp->{ID} eq $id)) { $result[-1]->{CODE} = $temp->{CODE}; last; } } } print Dumper(\@result);

    OUTPUT

    $VAR1 = [ { 'ID' => '1212', 'CODE' => '10', 'Number' => 'XY 'Name' => 'JOE' }, { 'ID' => '4456', 'Name' => 'MARI 'Number' => 'TY 'CODE' => '10' }, { 'CODE' => '6', 'Number' => 'HP 'Name' => 'JEAN 'ID' => '8765' } ];
    Bill
      I think your code is doing something in reverse, noticed that I need to keep the values in the node where CODE => 10. But if I change the value in "Number" and run the code, it gets overwritten from the value in the node with "6":
      use strict; use warnings; use Data::Dumper; my $test_data = [ {'ID' => '1212', 'Name' => 'JOE' , 'Number' => 'XY1', 'CODE' => +'6',}, {'ID' => '1212', 'Name' => '' , 'Number' => 'WW3', 'CODE' => ' +10',}, {'ID' => '4456', 'Name' => 'MARIA', 'Number' => 'TYX', 'CODE' => +'6',}, {'ID' => '4456', 'Name' => '' , 'Number' => 'TYX', 'CODE' => ' +10',}, {'ID' => '8765', 'Name' => 'JEAN' , 'Number' => 'HPO', 'CODE' => +'6',}, ]; my @result; foreach my $data ( @{ $test_data } ) { next if !$data->{Name}; my $id = $data->{ID}; push @result, $data; foreach my $temp ( @{ $test_data } ) { if (!$temp->{Name} and ($temp->{ID} eq $id)) { $result[-1]->{CODE} = $temp->{CODE}; last; } } } print Dumper(\@result);

      When it should be like this after running the code:
      {'ID' => '1212', 'Name' => 'JOE' , 'Number' => 'WW3', 'CODE' => ' +10',},
        You can fix this problem by adding one line.
        ... $result[-1]->{CODE} = $temp->{CODE}; $result[-1]->{Number} = $temp->{Number}; ...

        I thought that I understood your requirements, but now I am not sure. I now think that you want one output record for each ID. For each ID, the input should contain one named record and possible one unnamed one. Output for any other input would be undefined. We probably cannot make any assumptions about record order. In the single record case, we output that record. In the two record case, we output the unnamed record with the value of its name field replaced with the name from the other record.

        Bill
Re: Add value from one array element into another of same ID
by tybalt89 (Monsignor) on May 27, 2021 at 20:53 UTC
    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11133140 use warnings; my $test_data = [ {'ID' => '1212', 'Name' => 'JOE' , 'Number' => 'XY1', 'CODE' => +'6',}, {'ID' => '1212', 'Name' => '' , 'Number' => 'WW3', 'CODE' => ' +10',}, {'ID' => '4456', 'Name' => 'MARIA', 'Number' => 'TYX', 'CODE' => +'6',}, {'ID' => '4456', 'Name' => '' , 'Number' => 'TYX', 'CODE' => ' +10',}, {'ID' => '8765', 'Name' => 'JEAN' , 'Number' => 'HPO', 'CODE' => +'6',}, ]; my %lastid; my @test; for my $this ( @$test_data ) { if( my $previous = $lastid{ $this->{ID} } ) { $this->{$_} eq '' or $previous->{$_} = $this->{$_} for keys %$this +; } else { push @test, $lastid{ $this->{ID} } = { %$this } } } use Data::Dump 'dd'; dd 'test_data', $test_data, 'test', \@test;

    Outputs:

    ( "test_data", [ { CODE => 6, ID => 1212, Name => "JOE", Number => "XY1" }, { CODE => 10, ID => 1212, Name => "", Number => "WW3" }, { CODE => 6, ID => 4456, Name => "MARIA", Number => "TYX" }, { CODE => 10, ID => 4456, Name => "", Number => "TYX" }, { CODE => 6, ID => 8765, Name => "JEAN", Number => "HPO" }, ], "test", [ { CODE => 10, ID => 1212, Name => "JOE", Number => "WW3" }, { CODE => 10, ID => 4456, Name => "MARIA", Number => "TYX" }, { CODE => 6, ID => 8765, Name => "JEAN", Number => "HPO" }, ], )
Re: Add value from one array element into another of same ID
by vincent_veyron (Sexton) on May 28, 2021 at 20:27 UTC

    Hi,

    If you can fiddle with a database, this is very easy to solve with SQL; in Postgres, probably others, you can write :

    update tbldata set name = t1.name from tbldata t1 where tbldata.name i +s null and tbldata.id = t1.id;
    (assuming your data resides in tbldata with fields id, name, number, code)

    https://marica.fr
    Logiciel de gestion des sinistres assurances, des dossiers contentieux et des contrats pour le service juridique

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11133140]
Approved by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2024-04-24 12:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found