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

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.

Related

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

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.

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 does this specific value hold? Perl

my #array = #{$array_ref};
I'm not entirely sure what this array is storing. Can anyone care to explain to me what is happening? Thanks.
$array_ref presumably holds a reference to an array, which is different from an array itself. An array (named #some_array) has elements (unless it's empty). An array ref (named $some_arrayref) is a scalar value that is used to reference an actual array. The actual array may be a named array (as in, it has a variable name), or may be an anonymous array (as in, no named variable, but referred to exclusively by reference).
To use the elements held in the array referred to by $array_ref, you have to dereference it. One syntax is #$array_ref (read that as "The array (#) referred to by the scalar ($) named array_ref). I've never been a fan of jamming a bunch of sigils together (the #, $ and % symbols) when dereferencing. I feel it hampers readability of the code, particularly as the references become more complex or nested. Perl allows for curly brackets, which help to disambiguate and visually clarify what's going on. #{$array_ref} can be read as dereference as an array ( #{....} ) the array referred to by $array_ref.
From Perl's POD, have a look at perlreftut. It's a great starting point.
The code makes a top-level copy of the array referenced by $array_ref and stores it in #array.
To make a deep-level copy, use dclone from Storable:
use Storable;
#array = #{ dclone( $array_ref ) };
See perldoc Storable for details.
Storable is a standard module that is install with Perl. To see a list of all standard pargmatics and modules, see perldoc perlmodlib.
This is simply dereferencing the array ref. $array_ref contains a reference to an array, #array is set to the contents of the referenced array.
$ perl -E 'my $a = [1, 2, 3]; my #b = #{$a}; say $a; say #b'
ARRAY(0x100803ea0) # a reference to an array
123 # actual array
You need to read perlref.
In short, #array is the same array that $array_ref was pointing to. That is the most precise answer that I can give without knowing what got put into $array_ref.
$array_ref is a reference to a list / array.
so in order to dereference it in a list content and get all the contents, you use
#{$array_ref}
and this can be assigned into a different array / list like:
my #array = #{$array_ref};

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