How can I paginate a Perl array? - arrays

I don't know if the question is clear or not, anyway, I am reading some data from my google drive spreadsheet using Net::Google::Spreadsheet, and i fetch all the rows in an array as follow my #rows = $worksheet->rows, however, I want to ... let say divide #rows between other arrays, for example #rows has 200 elements, i want to give the first 50 to #array1 and the next 50 to #array2 and so on without using a counter (counter++; if (counter > 50) ...). Or let say i just want to get the elements between 70 and 110 for example.

You could use something like this:
my #array1 = #rows[0 .. 49];
my #array2 = #rows[50 .. 99];
This is referred to as data slicing. Refer to this documentation for more details.

The plain array slice answer is great. I just want to increase visibility of another (class/style of) solution, specifically Data::Page.
It has been said before that this code is "too simple" for CPAN, but I must disagree.
It can seem like overkill but it makes the stuff clean, predictable, and repeatable. When code starts to grow past the one-off stage, it can help quite a lot.
use strictures;
use Data::Page;
my #stuff = ( "a" .. "z" );
my $pager = Data::Page->new;
$pager->total_entries(scalar#stuff);
$pager->entries_per_page(6); # Arbitrary for demo, pick your own page size.
for my $p ( $pager->first_page .. $pager->last_page )
{
$pager->current_page($p);
for my $entry ( $pager->splice(\#stuff) )
{
print $entry, " is on page ", $pager->current_page, $/;
}
}
__END__
a is on page 1
--snip--
z is on page 5

Related

Group similar element of array together to use in foreach at once in perl

i have an array which contents elements in which some elements are similiar under certain conditions (if we detete the "n and p" from the array element then the similiar element can be recognised) . I want to use these similiar element at once while using foreach statement. The array is seen below
my #array = qw(abc_n abc_p gg_n gg_p munday_n_xy munday_p_xy soc_n soc_p);
Order of the array element need not to be in this way always.
i am editing this question again. Sorry if i am not able to deliver the question properly. I have to print a string multiple times in the file with the variable present in the above array . I am just trying to make you understand the question through below code, the below code is not right in any sense .... i m just using it to make you understand my question.
open (FILE, ">" , "test.v");
foreach my $xy (#array){
print FILE "DUF A1 (.pin1($1), .pin2($2));" ; // $1 And $2 is just used to explain that
} // i just want to print abc_n and abc_p in one iteration of foreach loop and followed by other pairs in successive loops respectively
close (FILE);
The result i want to print is as follows:
DUF A1 ( .pin1(abc_n), .pin2(abc_p));
DUF A1 ( .pin1(gg_n), .pin2(gg_p));
DUF A1 ( .pin1(munday_n_xy), .pin2(munday_p_xy));
DUF A1 ( .pin1(soc_n), .pin2(soc_p));
The scripting language used is perl . Your help is really appreciated .
Thank You.!!
Partitioning a data set depends entirely on how data are "similiar under certain conditions."
The condition given is that with removal of _n and _p the "similar" elements become equal (I assume that underscore; the OP says n and p). In such a case one can do
use warnings;
use strict;
use feature 'say';
my #data = qw(abc_n abc_p gg_n gg_p munday_n_xy munday_p_xy soc_n soc_p);
my %part;
for my $elem (#data) {
push #{ $part{ $elem =~ s/_(?:n|p)//r } }, $elem;
}
say "$_ => #{$part{$_}}" for keys %part;
The grouped "similar" strings are printed as a demo since I don't understand the logic of the shown output. Please build your output strings as desired.
If this is it and there'll be no more input to process later in code, nor will there be a need to refer to those common factors, then you may want the groups in an array
my #groups = values %part;
If needed throw in a suitable sorting when writing the array, sort { ... } values %part.
For more fluid and less determined "similarity" try "fuzzy matching;" here is one example.

How to determine if an element exists in a Perl 6 array

I thought there would have been a simple answer for this somewhere on the internet but it seems like I'm having trouble finding a solution. I'm first of all wondering if there's a simple method or function for this:
e.g. ~~ or array.contains() from Perl 5
It would also be nice to know how many different ways of achieving this result there are in Perl 6 as some might be better than others given the situation of the problem.
my #a = <foo bar buzz>;
say so 'bar' ∈ #a;
say so #a ∋ 'buzz';
# OUTPUT«True␤True␤»
As documented in http://doc.perl6.org/language/setbagmix and defined in https://github.com/rakudo/rakudo/blob/nom/src/core/set_operators.pm .
I believe that Set checks for equivalence, if you need identity you will have to loop over the array and === it.
You could turn the Array into a Set and use subscripts.
say #a.Set{'bar'};
# OUTPUT«True␤»
say #a.Set<bar buzz>;
# OUTPUT«(True True)␤»
Another way to do this, is:
my #a = <foo bar buzz>;
if 'bar' eq any(#a) {
say "yes";
}
# OUTPUT«yes␤»
Sub first documentation. sub first returns the matching element or Nil. Nil is a falsey value meaning you can use the result in a Bool context to determine if the array contains matching element.
my #a = 'A'..'Z';
say 'We got it' if #a.first('Z');
say 'We didn\'t get it' if !#a.first(1);
There are several adverbs to sub first which change the results. For instance to return the index instead of the element it is possible use the :k adverb. In this example we also topicalize the result for use within the if statement:
my #a = 'A'..'Z';
if #a.first('Q', :k) -> $index {
say $index;
}

How to copy data to an array in perl?

I am trying access the data from the database and copy the data to an array. This is my code,
$sth = $dbh->prepare("SELECT * FROM abcd WHERE id=100 ");
$sth->execute;
$N=$sth->rows;
print "$N\n";
while (my #row_val = $sth->fetchrow_array()){
my ($uniqid, $time, $current, $id ) = #row_val;
$y[k]=$current;
$k++;
}
for ($k=0;$k<$N;$k++) {
print "$y[k]\t";
}
But it displays the same value for all $y[k]. How to copy the data from database to an array in perl?
You are using a bareword here:
$y[k]=$current;
# ^--- here, the "k" is a bareword
If you use warnings this will give a warning
Unquoted string "k" may clash with future reserved word at foo.pl line 10.
Argument "k" isn't numeric in array element at foo.pl line 10.
And the "k" will be interpreted as a string, will be converted to a number, which will be zero 0, so all your data is stored in $y[0].
This is why it is a very bad idea to not turn warnings on.
What you probably want instead is to push the new values onto the array:
push #y, $current;
This is, IMO, preferable to using an index, since it does all that work for you. Usually, you only want to specifically get involved with array indexes if the indexes themselves are of value for you, such as when comparing array elements.
This also means that your subsequent for loop
for ($k=0;$k<$N;$k++) {
print "$y[k]\t";
}
Is better written
for (#y) {
print "$_\t";
}
Although this is better written with join:
print join "\t", #y;
As a final note, you should always use
use strict;
use warnings;
It takes a small amount of learning to overcome the additional noise when using these pragmas, but it is well worth it in terms of learning and reducing your time spent debugging. I usually say that not using these pragmas is like covering up the low oil warning lamp in your car: Not knowing about the errors does not solve them.
This behaviour is because you are putting everything to index "k" - not any number just "k",
it is only a coincidence that its working at all :) - the "same value" is the last value - isnt it ? :)
SOLUTION:
1) variables are written with $ - keep that in mind when accessing $yourArray[$variableWithIndex]
2) $y[k]=$current; # wrong! you are trying to access "k" index
correct: $y[$k]=$current;
Didnt tested it - but this should work:
$sth = $dbh->prepare("SELECT * FROM abcd WHERE id=100 ");
$sth->execute;
$N=$sth->rows;
print "$N\n";
$k=0; # init first!
while (my #row_val = $sth->fetchrow_array()){
my ($uniqid, $time, $current, $id ) = #row_val;
$y[$k]=$current; # dont forget the $
$k++;
}
for ($k=0;$k<$N;$k++) {
print "$y[$k]\t"; # dont forget the $
}

imputing index number in an array

I have 7 arrays that are defined in the following manner:
my #array1 = ();
..
my #array7 = ();
and then:
$array1[0] = "text goes here";
..
$array7[0] = "text goes here";
There are about 25 elements) in each of the seven arrays, that is, $array1[0] to $array1[24]. I need to frequently change the content of these arrays in various scripts. And sometimes, because the order of the arrays is essential, I need to rearrange the order of the array index or delete the elements at a position. This is a real pain in the ass, as I need to change the index of all subsequent arrays. Just to make it clear, if I delete array1[12], then I need to change $array1[13] to $array1[12] and that for all 7 arrays and for all subsequent index positions (or move the content of array1[13] to array1[12], etc.)
So my question is, is it possible to impute the index of the arrays so that I could switch around the arrays position without having to correct each array index afterwards? Something like this:
$array1[$_] = "text 1 goes here";
..
$array7[$_] = "other text 1 goes here";
and then:
$array1[$_] = "text 2 goes here";
..
$array7[$_] = "other text 2 goes here";
where $_ would be replaced by 1 for the first index of each of the 7 arrays and by 2 for the next element of each of the 7 arrays... (up to 24 elements).
Is there a solution to this problem other than using an hash and Tie::Hash?
EDIT
ok, let me clarify. I am looking for a script maintenance solution, no for a solution about the output of the script. I need to change the script myself (by hand) frequently and I do not want to change the numbers indexing all 24 positions of all 7 arrays by hand whenever I change something in these arrays. So my question was, is there a way to have the script impute the numbers indexing all positions of all arrays?
Using push as mvp was suggesting would be a proper solution. Are there any other solutions that could involve loops or something rather than using push 7X24 times?
Not quite sure what your question is. You are probably complicating things quite a bit. Are you looking for splice?
my #a = (1 .. 4);
splice #a, 2, 1; # remove 1 element, starting at index 2
# #a is now 1, 2, 4
splice can also insert elements and remove more than one element:
splice ARRAY or EXPR, OFFSET, LENGTH, LIST
You can use this approach:
my #array1;
push #array1, "text 1 goes here";
push #array1, "other text 1 goes here";
# ...
# or you can use loop as well:
for my $i (1..10) {
push #array1, "text 1 instance $i goes here";
}
# do it for another one:
my #array2;
push #array2, "text 2 goes here";
push #array2, "other text 2 goes here";
# ...
You can even do this (little bit nasty because of dynamic variables):
for my $i (1..7) {
for my $j (1..24) {
push #{"array$i"}, "text $i, instance $j goes here";
}
}

In Perl, how do I create a hash whose keys come from a given array?

Let's say I have an array, and I know I'm going to be doing a lot of "Does the array contain X?" checks. The efficient way to do this is to turn that array into a hash, where the keys are the array's elements, and then you can just say if($hash{X}) { ... }
Is there an easy way to do this array-to-hash conversion? Ideally, it should be versatile enough to take an anonymous array and return an anonymous hash.
%hash = map { $_ => 1 } #array;
It's not as short as the "#hash{#array} = ..." solutions, but those ones require the hash and array to already be defined somewhere else, whereas this one can take an anonymous array and return an anonymous hash.
What this does is take each element in the array and pair it up with a "1". When this list of (key, 1, key, 1, key 1) pairs get assigned to a hash, the odd-numbered ones become the hash's keys, and the even-numbered ones become the respective values.
#hash{#array} = (1) x #array;
It's a hash slice, a list of values from the hash, so it gets the list-y # in front.
From the docs:
If you're confused about why you use
an '#' there on a hash slice instead
of a '%', think of it like this. The
type of bracket (square or curly)
governs whether it's an array or a
hash being looked at. On the other
hand, the leading symbol ('$' or '#')
on the array or hash indicates whether
you are getting back a singular value
(a scalar) or a plural one (a list).
#hash{#keys} = undef;
The syntax here where you are referring to the hash with an # is a hash slice. We're basically saying $hash{$keys[0]} AND $hash{$keys[1]} AND $hash{$keys[2]} ... is a list on the left hand side of the =, an lvalue, and we're assigning to that list, which actually goes into the hash and sets the values for all the named keys. In this case, I only specified one value, so that value goes into $hash{$keys[0]}, and the other hash entries all auto-vivify (come to life) with undefined values. [My original suggestion here was set the expression = 1, which would've set that one key to 1 and the others to undef. I changed it for consistency, but as we'll see below, the exact values do not matter.]
When you realize that the lvalue, the expression on the left hand side of the =, is a list built out of the hash, then it'll start to make some sense why we're using that #. [Except I think this will change in Perl 6.]
The idea here is that you are using the hash as a set. What matters is not the value I am assigning; it's just the existence of the keys. So what you want to do is not something like:
if ($hash{$key} == 1) # then key is in the hash
instead:
if (exists $hash{$key}) # then key is in the set
It's actually more efficient to just run an exists check than to bother with the value in the hash, although to me the important thing here is just the concept that you are representing a set just with the keys of the hash. Also, somebody pointed out that by using undef as the value here, we will consume less storage space than we would assigning a value. (And also generate less confusion, as the value does not matter, and my solution would assign a value only to the first element in the hash and leave the others undef, and some other solutions are turning cartwheels to build an array of values to go into the hash; completely wasted effort).
Note that if typing if ( exists $hash{ key } ) isn’t too much work for you (which I prefer to use since the matter of interest is really the presence of a key rather than the truthiness of its value), then you can use the short and sweet
#hash{#key} = ();
I always thought that
foreach my $item (#array) { $hash{$item} = 1 }
was at least nice and readable / maintainable.
There is a presupposition here, that the most efficient way to do a lot of "Does the array contain X?" checks is to convert the array to a hash. Efficiency depends on the scarce resource, often time but sometimes space and sometimes programmer effort. You are at least doubling the memory consumed by keeping a list and a hash of the list around simultaneously. Plus you're writing more original code that you'll need to test, document, etc.
As an alternative, look at the List::MoreUtils module, specifically the functions any(), none(), true() and false(). They all take a block as the conditional and a list as the argument, similar to map() and grep():
print "At least one value undefined" if any { !defined($_) } #list;
I ran a quick test, loading in half of /usr/share/dict/words to an array (25000 words), then looking for eleven words selected from across the whole dictionary (every 5000th word) in the array, using both the array-to-hash method and the any() function from List::MoreUtils.
On Perl 5.8.8 built from source, the array-to-hash method runs almost 1100x faster than the any() method (1300x faster under Ubuntu 6.06's packaged Perl 5.8.7.)
That's not the full story however - the array-to-hash conversion takes about 0.04 seconds which in this case kills the time efficiency of array-to-hash method to 1.5x-2x faster than the any() method. Still good, but not nearly as stellar.
My gut feeling is that the array-to-hash method is going to beat any() in most cases, but I'd feel a whole lot better if I had some more solid metrics (lots of test cases, decent statistical analyses, maybe some big-O algorithmic analysis of each method, etc.) Depending on your needs, List::MoreUtils may be a better solution; it's certainly more flexible and requires less coding. Remember, premature optimization is a sin... :)
In perl 5.10, there's the close-to-magic ~~ operator:
sub invite_in {
my $vampires = [ qw(Angel Darla Spike Drusilla) ];
return ($_[0] ~~ $vampires) ? 0 : 1 ;
}
See here: http://dev.perl.org/perl5/news/2007/perl-5.10.0.html
Also worth noting for completeness, my usual method for doing this with 2 same-length arrays #keys and #vals which you would prefer were a hash...
my %hash = map { $keys[$_] => $vals[$_] } (0..#keys-1);
Raldi's solution can be tightened up to this (the '=>' from the original is not necessary):
my %hash = map { $_,1 } #array;
This technique can also be used for turning text lists into hashes:
my %hash = map { $_,1 } split(",",$line)
Additionally if you have a line of values like this: "foo=1,bar=2,baz=3" you can do this:
my %hash = map { split("=",$_) } split(",",$line);
[EDIT to include]
Another solution offered (which takes two lines) is:
my %hash;
#The values in %hash can only be accessed by doing exists($hash{$key})
#The assignment only works with '= undef;' and will not work properly with '= 1;'
#if you do '= 1;' only the hash key of $array[0] will be set to 1;
#hash{#array} = undef;
You could also use Perl6::Junction.
use Perl6::Junction qw'any';
my #arr = ( 1, 2, 3 );
if( any(#arr) == 1 ){ ... }
If you do a lot of set theoretic operations - you can also use Set::Scalar or similar module. Then $s = Set::Scalar->new( #array ) will build the Set for you - and you can query it with: $s->contains($m).
You can place the code into a subroutine, if you don't want pollute your namespace.
my $hash_ref =
sub{
my %hash;
#hash{ #{[ qw'one two three' ]} } = undef;
return \%hash;
}->();
Or even better:
sub keylist(#){
my %hash;
#hash{#_} = undef;
return \%hash;
}
my $hash_ref = keylist qw'one two three';
# or
my #key_list = qw'one two three';
my $hash_ref = keylist #key_list;
If you really wanted to pass an array reference:
sub keylist(\#){
my %hash;
#hash{ #{$_[0]} } = undef if #_;
return \%hash;
}
my #key_list = qw'one two three';
my $hash_ref = keylist #key_list;
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my #a = qw(5 8 2 5 4 8 9);
my #b = qw(7 6 5 4 3 2 1);
my $h = {};
#{$h}{#a} = #b;
print Dumper($h);
gives (note repeated keys get the value at the greatest position in the array - ie 8->2 and not 6)
$VAR1 = {
'8' => '2',
'4' => '3',
'9' => '1',
'2' => '5',
'5' => '4'
};
You might also want to check out Tie::IxHash, which implements ordered associative arrays. That would allow you to do both types of lookups (hash and index) on one copy of your data.

Resources