is there any way I can split the hashes in an array? - arrays

I have been trying to split an array with hashes but I am getting the following error
"use of uninitialised value $_ in split"
#array = (("forest:k233356"),("wi45one:1onetothree"));
foreach(#array)
($id,$pwd)=split(":",$_);
From the following code, I am not able to split the array and assign the following to $id and $pwd. Any leads will be appreciated!!

One possible approach is:
#array = ("forest:k233356", "wi45one:1onetothree");
%hash = map {split(":",$_, 2) } #array;
#array is an array of strings here. The map applies split ":", $_,2 on each element of the array, this splits each string into at most 2 fields (as mentioned in a comment below) and returns the results to %hash. In the end, the %hash will have ids as keys and pwds as values.
If you want more help you can read:
https://perldoc.perl.org/perldata.html#List-value-constructors
https://perldoc.perl.org/functions/map.html
https://perldoc.perl.org/functions/split.html

Other than the fact that your code didn't compile (and, therefore, can't have possibly given the error message that you show us) it was very close to the correct answer.
This is lightly cleaned up and extended (to actually put the data into the hash) but it's pretty similar to what you started with.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my %hash;
my #array = ("forest:k233356", "wi45one:1onetothree");
foreach (#array) {
my ($id, $pwd) = split(/:/, $_, 2);
$hash{$id} = $pwd;
}
say Dumper \%hash;
A few stylistic points:
I added a bit more whitespace in various places.
I removed the unnecessary parentheses where you define your array.
I made the first argument to split() into a regular expression.
I added 2 as the third argument to split() so the string is only ever split into two parts (it makes no difference with your existing data - but it's good to be safe).
None of these would have made any difference to the way your code worked - they just make your code that bit easier to read and understand.

Related

Return array value from one Perl script to another Perl script

I have 2 scripts, say main_script.pl and secondary_script.pl
First I am running the main_script.pl which calls seconday_script.pl.
Code looks below for main_script.pl:
#!/usr/bin/perl
use strict;
use warnings;
my $var1 = "val_1";
my $var2 = "val_2";
my $sec_script = "/home/shared/Vinod/Perl_Test/secondary_script.pl";
my $result = `perl $sec_script $var1 $var2`;
print "Result:$result\n";
secondary_script.pl
#!/usr/bin/perl
use strict;
use warnings;
my $arg1 = $ARGV[0];
my $arg2 = $ARGV[1];
....
....
# DO SOME OPEARTION BY USING THE ARGUMENTS PASSED FROM main_script.pl
# FINALLY CREATE AN ARRAY #data
print Dumper(\#data);
Here I can able to generate output in array #data. But how can I pass this #data values to main_script.pl so that it will get stored in result.
Since in main_script.pl I have declared result as an scalar variable. My data value from secondary_script.pl would be array, so should I make result as an array variable? and how can I capture the data in main_script.pl?
In the secondary script, you can join the values with a known seperator, say |. In het main script you can split the string you receive on that seperator to get your array.
To give an idea how that could work:
use Data::Dumper;
my #arr = ("1", "3", "", "5");
my $result = join ('|', #arr); # in secondary script. print to stdout
print STDERR Dumper(split( /\|/, $result)); # in main script: undo join
There is an edge case: if the first or last string of the array is an empty string, it will get lost and you have to do some extra tricks to account for that.
First, I suggest asking yourself why you're doing this. Do they really need to be two separate programs? Could one, perhaps, become a module which you can then load in the other?
Maybe there are good reasons for doing it like this. Personally, I doubt it.
But, assuming you decide that you need to do it how you're currently doing it, then we need to talk about "serialisation" and "deserialisation". Serialisation is the act of taking a complex internal data structure and turning it into a representation (often a string) which can be safely passed outside of your program. Deserialisation is the opposite - you take the external representation and turn it back into a data structure.
So in your secondary script you need a subroutine called serialise() that you call like this:
print serialise(#data);
And in your main script, you can have:
my #result_array = deserialise(`... your call ...`);
You have several options for (de)serialisation. You could make your own (de)serialiser using Perl built-ins like join() and split().
# This assumes you're sure that your array elements won't contain ':'.
# If that's a problem, then choose a different character.
sub serialise {
return join ':', #_;
}
sub deserialise {
return split /:/, $_[0];
}
You could use freeze() and thaw() subroutines from the standard Storable library:
use Storable qw[freeze thaw];
# Note that freeze() takes a reference and thaw() returns a reference.
sub serialise {
return freeze($_[0]);
}
sub deserialise {
return thaw($_[0]);
}
But I think the approach I would take would be to use a human-readable serialisation format like YAML or JSON.
In the first script, output one element per line:
print join "\n", #data;
Then, read from backticks in list context to get one output line per element, then remove the line separator:
my #array = `...`;
chomp( #array );
If a newline is significant, use some other separator (I tend to like form feeds).
But, instead of that, perhaps consider something along the modulino design where the data-generating program can act as both a module and a program. When another program needs it, it can load it as a module and call the right subroutine. No second program needed.
Beyond that, there are various interchange formats that might be appropriate. The particular modules I link to in this list may not be suitable for every task:
Storable
YAML
JSON
Sereal
many other things (binary JSON, msgpack, whatever)
Note that it's not always appropriate to reach for these formats right away. There's a cost associated with marshaling and unmarshalling the data. Profiling the hidden costs of JSON and HTTP(S) is an interesting read.
I'd tend to optimize for the "most friendly" output. The output format you choose decides how much pain the other side has to feel. My facile idea of simply printing to standard output means that everyone else has to treat your output as special. Maybe JSON fits in with what they're already doing elsewhere.
Or, you can output whatever they like. Have a way to do line-oriented output, JSON, XML, or whatever. Coming up with a way to do any format you choose means that you can easily add another format.

Why the array could not hold the keys of hash in the following perl script?

hash_test.pl
#a=("f","a","b");
$K{"f"}{"aa"}=1;
$K{"a"}{"aa"}=1;
$k{"b"}{"bb"}=1;
foreach(#a){
#c= sort keys %{$k{$_}};
}
print "#c\n";
foreach(#c) {...}
perl hash_test.pl
bb
I want to keep the keys of the hash into an array, so that I can use the array as an input for the following statements.
But it seemed that the assay #c just only hold the last element.
Could anyone tell me why or help me to improve the script?
You assign the array every time in the foreach, thus overwriting it every time. So you end up with only having the last thing assigned to it. If you move the print inside the foreach you'll see that they are all there.
To store those keys you need to add them to the array, not assign the array. I've corrected the typo $k to $K, and changed aa that goes with f to ff (expecting it to be a typo as well).
my #c;
foreach my $el (#a) {
push #c, sort keys %{$K{$el}};
}
print "#c\n";
This prints the line: ff aa bb. Every time through the loop all keys found in the hash for a particular array element are added to #c, each as a separate element. So #c will contain all bottom-level keys across the whole data structure.
However, there is more that I would like to suggest.
Always use strict; and use warnings; This is not pedantry but it directly helps. I never write code without them. The typo would be caught here, for example.
Use descriptive variable names. Specifically, single-letter variable names are just too easy to confuse, unless in very short loops or where it is crystal clear what they are. (For example, a typo like this couldn't really happen.) Most importantly, the code is going to be far nicer to work with. That generally results in better code.
Please use good indentation and spacing. It helps a lot, in many ways.
A useful core package for nested data structures is Data::Dumper, which can print the whole thing nicely formatted so we can see it. Try to add to the end of your code
use Data::Dumper;
print Dumper(\%K);
There are yet others that do the same or similar.
Here is another way to do what you ask.
my #lowest_keys = map { sort keys %{$K{$_}} } #a;
I call them lowest_keys to emphasize that these are the ones from the last hash in your data structure, the bottom of it. The map applies processing in the block { ... } to each element of #a in turn, returning a list with all these results. (If any one result itself is a list, with more elements than one, it gets merged into the overall output list. So this may create the output list with many more elements than the input.) This list can then be assigned to an array, as above, or passed on to another function that expects a list as input, or interated over.
The map is generally used to transform an array into another, by doing to each element what is in { ... } block. Its close cousin grep is used to filter, so passing through only the elements of the input list for which the condition in { ... } evaluates to true, forming the output list. For example, filter out undefined array elements: my #good = grep { defined } #all
Variable names are case sensitive, so %K... and %k... are not the same.
Always use
use strict;
use warnings;
and declare your variables with my. That prevents you from making this kind of mistakes.

Array of hashes of arrays of references to arrays

I have a problem accessing to data, described in the title of the question.
The structure of the data is the following:
my #structure = (
{
"Name" => "Test",
"Param1" => 1,
"Data1" => \#test1,
"Data2" => [\#test3],
"Data3" => [\#test1, \#test2],
},
...
);
And I need to access the 1st (#0) element of the array #test3.
I tried something like this:
my #array = #{$_->{'Data2'}}[0];
print $array[0];
But then I get a reference to the array (ARRAY(0x239c3c)) instead of the required meaning. I feel that there's something global that I don't understand here.
Couldn't you explain how and why I should address to the required meaning?
Thanks a lot.
use strict;
use warnings;
my #test1 = (1,2,3);
my #test2 = (4,5,6);
my #test3 = (7,8,9);
my #structure = (
{
"Name" => "Test",
"Param1" => 1,
"Data1" => \#test1,
"Data2" => [\#test3],
"Data3" => [\#test1, \#test2],
}
);
print $structure[0] # $structure is an array, you want first element...
->{'Data2'} # ...which is a hash reference, you want value for 'Data2'...
->[0] # ...which is holding an array reference to (one element) array #test3...
->[0]; # ...which is an array reference, and you want first element
prints 7.
print $structure[0]{'Data2'}[0][0]; will work!
you just forget the last level!
$_->{'Data2'} is reference to an anonymous array.
#{$_->{'Data2'}}[0] gives you the first element of anonymous array, a reference to another array!
Your code would work if you had "Data2" => \#test3, instead of "Data2" => [\#test3],
You can use the syntactic sugar of -> to get the reference you want. It's much cleaner than using parentheses.
And I need to access the 1st (#0) element of the array #test3
Okay, in your structure, it'd be:
my $element = $structure[0]->{Data2}->[0]->[0];
This is equivalent, but harder to read:
my $element = ${${${$structure[0]}{Data2}}[0]}[0];
And, in this particular case, you can eliminate the syntactic sugar ->:
my $element = $structure[0]{Data2}[0][0];
However, I don't know if that makes it easier to read, and there are cases where the default way of parsing that might not do exactly what you want. I tend to squeeze everything together when I'm referring to an array of arrays, but that's it:
my $element = $array[0][3]; #This is okay
my $element = $array[0]->[3]; #Same as above, but with -> syntax
Otherwise, I'll use the syntactic sweetener of ->.
Here's an explanation of the syntax:
$structure[0] is a reference to the first item of your #structure array. This contains a reference to a hash.
$structure[0]->{Data2} is a reference to the hash element Data2 in the first item in your #structure array. This is a reference to an array.
$structure[0]->{Data2}->[0] is a reference to the first array reference in the Data2 hash element in the first element (0) of your #structure array. This is your #test3 array.
$structure[0]->{Data2}->[0]->[0] is the first element of the #test3 array.
I also recommend you use the Data::Dumper module as you write your code. It'll help you see what structure you're referencing and help point out any errors in the levels. For example:
use Data::Dumper;
print $structure[0]->{Data2}->[0] . "\n";
Will show you that this is a reference to another array layer.
print "$structure[0]\n";
Will show you this is a reference to a hash where each item contains references to arrays of arrays.
Whenever you use these highly complex structures, it screams for using object oriented Perl coding. I'm not 100% sure what you're doing, so I can't help you with your objects, but highly complex structures tend to break and are almost impossible to maintain.
For example, we now do this:
use strict;
use warnings;
my $Foo = "bar";
print "Foo = $foo\n";
This quickly detects that I misspelled my variable name by doing $foo instead of $Foo. This was a big improvement over older versions of Perl where this type of error checking couldn't be done.
However, now look at this:
use strict;
use warnings;
my %hash;
$hash{Foo} = "bar";
print "Foo = $hash{foo}\n";
We've lost the compilation error checking. %hash is a legitimate variable, but $hash{Foo} is defined and not $hash{foo}. In this case, use strict does nothing for you.
Using object oriented Perl returns this error check:
use strict;
use warnings;
use Object::Foo;
my $hash = Object::Foo->new;
$hash->Foo(bar);
print "Foo = " . $hash->foo . "\n";
Whoops! The method should be Foo and not foo. However, Perl is able to detect that the method foo doesn't exist and will once again generate an error on compile.
In the above case, it's pretty straight forward, but you're dealing with an array of hashes of arrays of arrays. The possibility of an error is multiplied by 100 with each reference access. And, because you have to spell out these references over and over in your code, an error can be located in dozens of different places. Even if you could code this initially and then comment the entire structure in your code, it'll be impossible to maintain this when you come back to this after three months.
Take a look at Perldoc's Tutorials and go through the ones for object oriented programming.

What is the 'right' way of deleting array elements in Perl?

I have an array containing a set of elements. The order of elements is irrelevant - I use an array since it's the simplest data structure I know in Perl.
my #arr = ...
while (some condition) {
# iterate over #arr and remove all elements which meet some criteria
# (which depends on $i)
}
I know of splice() but I think it's not good using it while iterating. delete for array elements seems deprecated. Perhaps use grep on #arr into itself (#arr = grep {...} #arr)?
What is the best practice here?
Perhaps use a hash (although I don't really need it)?
Your idea of using grep is good
#arr = grep { cond($i++); } #arr;
According to the docs, calling delete on array values is deprecated and likely to be removed in a future version of Perl.
Alternatively, you can build a list of needed indices and assign the slice to the original array:
#arr = #arr[ #indices ];
You can read more about slices in perldata.

perl foreach looping arrays, simple question

Really simple perl question, but confusing me greatly.
foreach $val (#{$obj->something()}) {
# this works
}
#array = $obj->something();
foreach $val (#array) {
# this does not
}
What do i need to do to make the second work (i.e: assign the array seperately), i've used the first form a fair bit but dont really understand what it does differently.
Probably:
#array = #{$obj->something()};
From the first example, it looks like $obj->something() returns an array reference, you'll need to dereference it.
Also, you should really use strict; and use warnings;, and declare your variables like
my #array = #{$obj->something()};
foreach my $val (#array) {
# this does not
}
This will make it much easier to find mistakes (although probably not this one), even in a three line script.

Resources