CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
How to change the values at array indexes

 



Tejas
User

Dec 3, 2014, 9:48 AM

Post #1 of 16 (9040 views)
How to change the values at array indexes Can't Post


Code
 



use strict;
use warnings;



my %hash;

$hash{""}{"01"}{"14"} = "OTC";
$hash{"04"}{"01"}{"14"} = "CTO";
$hash{"04"}{"01"}{"15"} = "BAC";

my @array = ("01","04","CTO","14-15");
myprint(\@array);

sub myprint {
my ($array) = @_;
my $ppcl_id = $array->[0];
my $curr_cc = $array->[1];
my $etid = pop (@{$array});
my @etid_list = split /-/,$etid ;
#my @etid_list = uniq @etid_dup;
my $arraySize = scalar (@etid_list);
if ($arraySize > 1) {
for my $i (0..$#etid_list) {
my $counter =2;
my $proxy_cc = ""; # empty
while( $counter >= 1 )
{
$counter --;
if ( not exists $hash{$proxy_cc}{$ppcl_id}{$etid_list[$i]} ) { # If empty doesnt exists then go to print block
$proxy_cc = $curr_cc;
next ;
}

else { #if empty exists
#print "In Null Exists \n";
my $dsid = $hash{$proxy_cc}{$ppcl_id}{$etid_list[$i]} ;
my $old_value = $array->[1];
$array->[2] = $dsid; # and then again recrate the array with old values and go to pfrint block
$array->[1] = $proxy_cc; #Insert empty
$proxy_cc = $old_value; #Set to old value,when this is called again we get the old value printed
}

for $i(0..$#array) {
print "$array->[$i] \t" ;
}
print "$etid_list[$i]\n";


}
}
}

}



Im trying to chnage the array values as per the values i got from hash ,as i need to print this combination too.
And then iam just re creating the same array with original values


Also, if there is no null key present in the hash, i just need to go to print
and print normally

All this circus is just to find if a null key exists , if yes print,
If no null key , just go ahed normally to print block

Any other simple way to acheive it ?

Im checking this only for hiplenated values from array , thats why we have split and arraysize condition



Expected output

Quote
01 OTC 14
01 04 CTO 14
01 04 BAC 15

Current output
Both are same though

Quote
01 OTC 14
01 04 CTO 14
01 04 BAC 15



Quote
To Keep it simple this check should nt happen for anything that doesnt have null key ..



(This post was edited by Tejas on Dec 3, 2014, 10:18 AM)


Tejas
User

Dec 3, 2014, 10:25 AM

Post #2 of 16 (9033 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post

because for non null keys combined with with other keys


1. $hash{""}{"01"}{"14"} = "OTC";
2. $hash{"04"}{"01"}{"14"} = "CTO";

3. $hash{"04"}{"01"}{"15"} = "BAC";

4. $hash{"41"}{"01"}{"15"} = "MAC";

There are some keys which i do not need a check and they can be directly printed

THe combination with 01 and 15 do not really have a null key , so check is nt really needed

As per current code , even these both get checked


Thanks
Tejas


BillKSmith
Veteran

Dec 4, 2014, 6:28 PM

Post #3 of 16 (8938 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post

I really do not understand your question. The following code seems to do the same thing as yours, but in a much simpler way. Hopefully, it will serve as baseline for discussion of required changes.


Code
use strict; 
use warnings;
use Readonly;
Readonly::Array my @ARRAY => ( "01", "04", "CTO", "14-15" );
myprint( \@ARRAY );

BEGIN{
use Readonly;
my %HASH;
$HASH{""}{"01"}{"14"} = "OTC";
$HASH{"04"}{"01"}{"14"} = "CTO";
$HASH{"04"}{"01"}{"15"} = "BAC";
Readonly::Hash %HASH => %HASH;

sub myprint {
my ($ppcl_id, $cur_cc, $value, $etid_string) = @{$_[0]};
foreach my $etid (split /-/, $etid_string){
CC:
foreach my $cc ('', $cur_cc) {
next CC unless (
exists $HASH{$cc}
and exists $HASH{$cc}{$ppcl_id}
and exists $HASH{$cc}{$ppcl_id}{$etid}
);
my $dsid = $HASH{$cc}{$ppcl_id}{$etid};
print "$ppcl_id\t$cc\t$dsid\t$etid\n";

} # cc loop
} # etid loop
} # sub myprint
} # BEGIN BLOCK


Notes:
Readonly is not required. I am using it as a debug tool to verify that neither the array nor hash are ever modified.

The BEGIN block is not truely needed. It effectively limits the scope of the HASH to the subroutine, yet only does the initialization once.

The style of the if statement avoids the problem of autovivification. As soon as one 'exists' fails, perl sets the whole expression to false and stops checking.
Good Luck,
Bill


Tejas
User

Dec 5, 2014, 10:41 AM

Post #4 of 16 (8739 views)
Re: [BillKSmith] How to change the values at array indexes [In reply to] Can't Post

Hi Bill
Thnaks for the code.


Question 1 :
Readonly::Array my @ARRAY => ( "01", "04", "CTO", "14-15" );

$HASH{""}{"01"}{"14"} = "OTC";
$HASH{"04"}{"01"}{"14"} = "CTO";
$HASH{"04"}{"01"}{"15"} = "BAC";

We donot have a empty key for "BAC" (i.e $HASH{"04"}{"01"}{"15"} = "BAC";)
So it should nt enter the loop at all,it should be checked in the array .
We need not bother if its there or not in hash , just print it as it doesnt have a blank combination


Here its checking for both blank and curr_cc,
Curr_cc check should nt be done if no blank

A stupid approach that i had was


Code
		 
if ( exists $hash->{""}{$ppcl_id}{$etid_b} )
{
for ( "", $curr_cc ) {
if ( exists $hash->{$_}{$ppcl_id}{$etid_b} )
{
print_to_excel"$ppcl_id,$_,$hash->{$_}
{$ppcl_id}{$etid_b},$etid_b ;
}
}
}

else
{
print"$ppcl_id,$curr_cc,$dsid,$etid_b \n";
}





Question 2 :

if the func call is

Code
myprint( 1,\@ARRAY );

How will the handle the variables and forloop in your code.
Or do we need to change code altogether


Thanks
tejas


FishMonger
Veteran / Moderator

Dec 5, 2014, 11:47 AM

Post #5 of 16 (8725 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post

Having an empty string as a hash key should be considered a bug that needs to be fixed. Intentionally using an empty string as a hash key is IMO silly.


Tejas
User

Dec 5, 2014, 11:50 AM

Post #6 of 16 (8722 views)
Re: [FishMonger] How to change the values at array indexes [In reply to] Can't Post

What could be the work around then
We have a requirement that there can be no values
Actually we are building hashes from a csv file
And there might or might not be a value in each line and each cell
Ex
1,2,3,5
,,1,2
,1,3,4,
1,3,4,

Thanks
Tejas


Laurent_R
Veteran / Moderator

Dec 5, 2014, 3:14 PM

Post #7 of 16 (8686 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post

But it does not really make sense to have in a hash an empty key for an existing and defined value. This might indicate that you hash is a poor data strructure for your input data. Maybe you could possibly use for that key some default value that you are sure cannot exist in your data, but that creates special cases to be manages and still is a bit awkward.


BillKSmith
Veteran

Dec 5, 2014, 3:30 PM

Post #8 of 16 (8684 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post

Of course, I agree with FishMonger's comments and will have more to say in a later post. For now, lets continue to work on your requirements.

I think your latest code resolved my problem. It will not work correctly because you still have not learned how to avoid autovivification. I assume that the print_to_excel is the real output. The 'print' is effectively an error message ('warn' would be better).

I think it would be clearer to eliminate the innermost loop and show the logic explicitly. The test for existence is complicated and now needed in several places. This is a good reason to make it a subroutine. Note that it shares direct access to the hash with the myprint subroutine.

In answer to question 2, I have added an otherwise useless flag to the argument list of myprint.

Here is my implementation of my understanding of your reqirements.


Code
 
use strict;
use warnings;
use Readonly;
Readonly::Scalar my $EMPTY => '';
Readonly::Array my @ARRAY => ( "01", "04", "CTO", "14-15" );

myprint(1, \@ARRAY );

BEGIN {
use Readonly;
my %HASH;
$HASH{$EMPTY}{"01"}{"14"} = "OTC";
$HASH{ "04" }{"01"}{"14"} = "CTO";
$HASH{ "04" }{"01"}{"15"} = "BAC";
Readonly::Hash %HASH => %HASH;

sub myprint {
my $tejas_flag = shift;
my ( $ppcl_id, $cur_cc, $value, $etid_string ) = @{ $_[0] };
foreach my $etid ( split /-/, $etid_string ) {
if (exists_in_hash($EMPTY, $ppcl_id, $etid)){
my $dsid = $HASH{$EMPTY}{$ppcl_id}{$etid};
print_to_excel( $ppcl_id, $EMPTY, $dsid, $etid );
if (exists_in_hash($cur_cc, $ppcl_id, $etid)) {
$dsid = $HASH{$cur_cc}{$ppcl_id}{$etid};
print_to_excel( $ppcl_id, $cur_cc, $dsid, $etid );
}
}
else {
my $dsid = $EMPTY;
if (exists_in_hash($cur_cc, $ppcl_id, $etid)){
$dsid = $HASH{$cur_cc}{$ppcl_id}{$etid};
}
warn "Cannot send to Excel: $ppcl_id\t$cur_cc\t$dsid\t$etid";
} # else block
} # etid loop
} # sub myprint

sub exists_in_hash{
my ($l1, $l2, $l3) = @_;
return (
exists $HASH{$l1}
and exists $HASH{$l1}{$l2}
and exists $HASH{$l1}{$l2}{$l3}
);
} # exists_in_hash
} # BEGIN BLOCK

sub print_to_excel {
# Stub to simulate excel interface
local $" = "\t";
print "print_to_excel: @_\n";
}


Output:

Code
print_to_excel: 01              OTC     14 
print_to_excel: 01 04 CTO 14
Cannot send to Excel: 01 04 BAC 15 at C:\Documents and Settings\
bill\Perl\guru\tejas.pl line 34.

Good Luck,
Bill


FishMonger
Veteran / Moderator

Dec 5, 2014, 7:13 PM

Post #9 of 16 (8673 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post


In Reply To
What could be the work around then
We have a requirement that there can be no values
Actually we are building hashes from a csv file
And there might or might not be a value in each line and each cell
Ex
1,2,3,5
,,1,2
,1,3,4,
1,3,4,

Thanks
Tejas


Using the field values as the hash keys instead of the column headers is clear sign of a poorly designed data structure and if there is an expectation of any of the fields being empty there by creating an empty sting as a hash key is crazy.


Tejas
User

Dec 5, 2014, 8:23 PM

Post #10 of 16 (8659 views)
Re: [FishMonger] How to change the values at array indexes [In reply to] Can't Post

Hi.

As i have told you that i have an csv input file and the data is something
like this

Quote
1,2,3,5
,,1,2
,1,3,4,
1,3,4,

which cant be avoided as we get this from clients.
But , as per ur good idea, we can use a string termed "Blank" for handling all these cases.
In essence all this has to be processed , as the file sent by clients have this pattern

Thanks
Tejas
Bu


BillKSmith
Veteran

Dec 7, 2014, 3:43 PM

Post #11 of 16 (8129 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post

I have considered alternate data structures. Usually a CSV file is stored as either an array of hashes or a hash of arrays. In either case, the column headings are used as keys and the data as values. In your case, because you are doing so much searching, you were probably right to use the data as keys. I do believe that you could simplify code by reordering the hierarchy of your hash. Choose a non-null string (perhaps 'N/A') as the value of fields that missing from your file.
Good Luck,
Bill


Tejas
User

Dec 7, 2014, 10:52 PM

Post #12 of 16 (8114 views)
Re: [BillKSmith] How to change the values at array indexes [In reply to] Can't Post

Can u show me an example
A sample snippet for hash of arrays for values and headings of a cab


Thanks
Tejas


BillKSmith
Veteran

Dec 8, 2014, 11:48 AM

Post #13 of 16 (8052 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post

I created a csv file (sample.csv) which contains the data used in your examples.

Code
ppl,cc,et,ds 
01,,14,OTC
01,04,14,CTO
01,04,15,BAC


The following code reads it into the hash that you use.

Code
use strict; 
use warnings;
use Data::Dumper;
open my $HANDLE, '<', 'sample.csv' or die 'cannot open sample';
my %hash;
<$HANDLE>; # ignore column headers
while (my $line = <$HANDLE>) {
chomp $line;
my ($ppl, $cc, $et, $ds) = map {$_ eq '' ? 'N/A' : $_} split /,/, $line;
$hash{$cc}{$ppl}{$et} = $ds;
}
print Dumper( \%hash );


Output:

Code
[ 
$VAR1 = {
'N/A' => {
'01' => {
'14' => 'OTC'
}
},
'04' => {
'01' => {
'15' => 'BAC',
'14' => 'CTO'
}
}
};

/code]

The following code reads the same csv file into the hash-of-Arrays that you requested.


Code
use strict; 
use warnings;
use Data::Dumper;
open my $HANDLE, '<', 'sample.csv' or die 'cannot open sample';
my %hash;
my @headers = split /,/, <$HANDLE>;
chomp @headers;
while (my $line = <$HANDLE>) {
chomp $line;
my @fields = split /,/, $line;
for my $i (0..$#fields) {
$fields[$i] = 'N/A' if $fields[$i] eq '';
push @{$hash{$headers[$i]}}, $fields[$i];
}
}
print Dumper( \%hash );


Output:

Code
$VAR1 = { 
'et' => [
'14',
'14',
'15'
],
'cc' => [
'N/A',
'04',
'04'
],
'ds' => [
'OTC',
'CTO',
'BAC'
],
'ppl' => [
'01',
'01',
'01'
]


For completeness, here is the code to read the save csv file into an Array-of_Hashes.


Code
use strict; 
use warnings;
use Data::Dumper;
open my $HANDLE, '<', 'sample.csv' or die 'cannot open sample';
my @AoH;
my @headers = split /,/, <$HANDLE>;
chomp @headers;
while (my $line = <$HANDLE>) {
chomp $line;
my %hash;
my @fields = split /,/, $line;
@hash{@headers} = map {$_ eq '' ? 'N/A' : $_} @fields;
push @AoH, \%hash;
}
print Dumper( \@AoH );


Output:

Code
$VAR1 = [ 
{
'et' => '14',
'cc' => 'N/A',
'ds' => 'OTC',
'ppl' => '01'
},
{
'et' => '14',
'cc' => '04',
'ds' => 'CTO',
'ppl' => '01'
},
{
'et' => '15',
'cc' => '04',
'ds' => 'BAC',
'ppl' => '01'
}
];


As I said in my previous post, none of these are ideal for your application.
I am still working on a better data structure and the code to demonstrate its advantage.
Good Luck,
Bill


BillKSmith
Veteran

Dec 9, 2014, 6:41 AM

Post #14 of 16 (7730 views)
Re: [BillKSmith] How to change the values at array indexes [In reply to] Can't Post

Reordering the levels of the hash allows the myprint subroutine to deal with one parameter at a time. Autovivification is never an issue.


Code
use strict; 
use warnings;
use Readonly;
Readonly::Scalar my $EMPTY => 'N/A';
Readonly::Array my @ARRAY => ( "01", "04", "CTO", "14-15" );
use Data::Dumper;

myprint(1, \@ARRAY );

BEGIN {
use Readonly;
open my $HANDLE, '<', 'sample.csv' or die 'cannot open sample';
my %hash;
<$HANDLE>; # ignore column headers
while (my $line = <$HANDLE>) {
chomp $line;
my ($ppl, $cc, $et, $ds)
= map {$_ eq '' ? 'N/A' : $_}
split /,/, $line;
$hash{$ppl}{$et}{$cc} = $ds; # Major Change!
}
close $HANDLE;
Readonly::Scalar my $EMPTY => 'N/A';
Readonly::Scalar my $HASH => {%hash};
%hash = ();

sub myprint {
my $tejas_flag = shift;
my ( $ppcl_id, $cur_cc, $value, $etid_string ) = @{ $_[0] };
print Dumper( $HASH);
if (!exists $HASH->{$ppcl_id}) {
warn "ppcl_id = $ppcl_id not in hash\n";
exit;
}
my $ref = $HASH->{$ppcl_id};
foreach my $etid ( split /-/, $etid_string ) {
if (!exists $ref->{$etid}) {
warn "etid = $etid not in hash\n";
next;
}
my $ref = $ref->{$etid};
if (!exists $ref->{$EMPTY}) {
warn "cur_cc = 'empty' not in hash for etid = $etid\n";
next;
}
my $dsid = $ref->{$EMPTY};
print_to_excel( $ppcl_id, $EMPTY, $dsid, $etid);
if (!exists $ref->{$cur_cc}) {
warn "cur_cc = $cur_cc not in hash\n";
next;
}
$dsid = $ref->{$cur_cc};
print_to_excel( $ppcl_id, $cur_cc, $dsid, $etid);
}
}
}

sub print_to_excel {
# Stub to simulate excel interface
local $" = "\t";
print "print_to_excel: @_\n";
}


Output:

Code
print_to_excel: 01      N/A     OTC     14 
print_to_excel: 01 04 CTO 14
cur_cc = 'empty' not in hash for etid = 15

Good Luck,
Bill


Tejas
User

Jan 9, 2015, 10:07 AM

Post #15 of 16 (5765 views)
Re: [BillKSmith] How to change the values at array indexes [In reply to] Can't Post

In both the cases AOH and HOA

The order of the headings are not inline


Quote
$VAR1 = {
'et' => [
'14',
'14',
'15'
],
'cc' => [
'N/A',
'04',
'04'
],
'ds' => [
'OTC',
'CTO',
'BAC'
],
'ppl' => [
'01',
'01',
'01'
]


while we habve the order in file as ppl,cc,et,ds

and same below

Quote

$VAR1 = [
{
'et' => '14',
'cc' => 'N/A',
'ds' => 'OTC',
'ppl' => '01'
},
{
'et' => '14',
'cc' => '04',
'ds' => 'CTO',
'ppl' => '01'
},
{
'et' => '15',
'cc' => '04',
'ds' => 'BAC',
'ppl' => '01'
}
];



Can we maintain the sequence, as we have to print it sequentially?

Thanks
Tejas


Laurent_R
Veteran / Moderator

Jan 11, 2015, 11:56 PM

Post #16 of 16 (5738 views)
Re: [Tejas] How to change the values at array indexes [In reply to] Can't Post

Not sure to understand the question, but the bottom line is that hashes have no order.

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives