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!
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. | [reply] [d/l] [select] |
|
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.
| [reply] [d/l] |
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]
| [reply] [d/l] [select] |
|
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} };
| [reply] [d/l] [select] |
|
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]
| [reply] [d/l] [select] |
|
|
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]]
| [reply] [d/l] [select] |
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'
}
];
| [reply] [d/l] [select] |
|
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',},
| [reply] [d/l] [select] |
|
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.
| [reply] [d/l] |
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" },
],
)
| [reply] [d/l] [select] |
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
| [reply] [d/l] |
|
|