Creating an array of arrays for GD::Graph - arrays

All right, I'm trying to make an array of arrays in Perl for use with the GD::Graph module. And I can't figure out why the following is not valid array of arrays for GD::Graph.
my #t = (1, 2, 3, 4);
my #g = (2, 4, 5, 6);
my #data = (#t, #g);
I've also tried constructing the data like below, and it still does not like it.
my #data;
push #data, #t;
push #data, #g;
I want to keep my values in seperate arrays and just combine them for use with GD::Graph, because that is what I've found to be the easiest way, even if it is ugly. How would I go about creating a valid structure for use with GD::Graph, that is created on the fly?
It complains about it here.
my $gd = $graph->plot(\#data) or die $graph->error;

Looks like #data is just a single dimension array with 8 elements.
You can define array of arrays by using the anonymous array constructor []
my #data = (
[1, 2, 3, 4],
[2, 4, 5, 6]
);

For me using array references did the trick
my #t = (1, 2, 3, 4);
my #g = (2, 4, 5, 6);
my #data = (\#t, \#g);
and the plot the chart with fro example:
my $graph = new GD::Graph::lines(800,600 );
my $gd = $graph->plot( \#data );
open OUT, ">","whatever.png" or die "Couldn't open for output: $!";
binmode(OUT);
print OUT $gd->png( );
close OUT;

Related

Trying to clear a array when all values are inserted on other array

So im trying to clear the array NewPassagers when all values inside passed to OnBusPassagers.
Its for a FiveM script btw!
if GetVehicleNumberOfPassengers(veh) > 1 then
for i = 1, #NewPassagers, 1 do
table.insert(OnBusPassagers, NewPassagers)
end
NewPassagers = nil
end
Maybe lets do a while, table.insert() and table.remove() do the job...
local tab1 = {1, 2, 3, 4, 5, 6, 7, 8, 9}
local tab2 = {}
while #tab1 ~= 0 do
-- Reverse order
-- table.insert(tab2, table.remove(tab1))
-- Same order
table.insert(tab2, table.remove(tab1, 1))
end
return tab2
...table.remove({}[, pos]) removes last key/value by default without pos and returning that value for table.insert().

Perl - function with two array arguments

I have troubles using a function in Perl.
My function has 2 arguments which are arrays :
sub get_coordinate {
my (#array_col, #array_lin) = (#_);
do some stuff
}
I call it this way :
$index_col = int(rand(10));
$index_lin = int(rand(10));
#array_col = (0,0,0,0,0,0,0,0,0,0);
#array_lin = (0,0,0,0,0,0,0,0,0,0);
$array_col[$index_col] = 1;
$array_lin[$index_lin] = 1;
get_coordinate(#array_col, #array_lin);
My problem is that I get the error message : Use of uninitialized value within #array_lin in numeric eq (==) at
switch.pl line 82 (#1)
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
I don't understand why #array_col is initialized an not #array_lin.
When I print #array_col and #array_lin inside the function this way :
print "#array_col\n#array_lin\n";
I get : 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0
Any idea ?
Thx,
SLP
In Perl, all lists are flat. These two lists are equivalent.
( 1, 2, ( 3, 4, ( 5 ), (6, 7), 8), (), )
( 1, 2, 3, 4, 5, 6, 7, 8 )
The same thing also happens when take several arrays and stick them in a list.
my #foo = (1, 2, 3);
my #bar = (4, 5, 6);
my #new = (#foo, #bar); # 1, 2, 3, 4, 5, 6
When you pass things to a function, those things get turned into a list of arguments. Therefore, the arrays will both end up in one list, like #foo and #bar above.
frobnicate(#foo, #bar);
When you assign something in list context, the entire list will be assigned left to right. For scalars in the list on the left-hand-side this means they will get their values. But as soon as there is an array, this will be greedy. It will suck up all the remaining values.
my ($one, $two, #rest, $will_be_undef) = (1, 2, 3, 4, 5, 6);
The values will be assigned like this:
$one = 1;
$two = 2;
#rest = ( 3, 4, 5, 6 );
$will_be_undef = undef;
What you need to do to pass two arrays is to take references, and dereference them in our function.
frobnicate( \#foo, \#bar );
sub frobnicate {
my ($first_array, $second_array) = #_;
my #foo = #{ $first_array };
my #bar = #{ $second_array };
...
}
Assigning to several arrays (or hashes) doesn't do what you think:
my (#array1, #array2) = ...
The first array gets all the elements. How should it know where to stop?
You can use array references instead:
sub get_coordinate {
my ($arr1, $arr2) = #_;
my #arr1 = #$arr1;
my #arr2 = #$arr2;
...
}
get_coordinate(\#array1, \#array2);

Perl - Filter function for arrays

i am trying to create a subroutine that does the following :
Takes two arrays as input (Filter, Base)
Outputs only the values of the second array that do not exist in the first
Example :
#a = ( 1, 2, 3, 4, 5 );
#b = ( 1, 2, 3, 4, 5, 6, 7);
Expected output : #c = ( 6, 7 );
Called as : filter_list(#filter, #base)
###############################################
sub filter_list {
my #names = shift;
my #arrayout;
foreach my $element (#_)
{
if (!($element ~~ #names )){
push #arrayout, $element;
}
}
return #arrayout
}
Test Run :
#filter = ( 'Tom', 'John' );
#array = ( 'Tom', 'John', 'Mary' );
#array3 = filter_list(#filter,#array);
print #array3;
print "\n";
Result :
JohnJohnMary
Can anyone help? Thank you.
You can't pass arrays to subs, only scalars. So when you do
my #filtered = filter_list(#filter, #base);
you are really doing
my #filtered = filter_list($filter[0], $filter[1], ..., $base[0], $base[1], ...);
As such, when you do
my #names = shift;
you are really doing
my #names = $filter[0];
which is obviously wrong.
The simplest solution is to pass references to the arrays.
my #filtered = filter_list(\#filter, \#base);
A hash permits an efficient implementation (O(N+M)).
sub filter_list {
my ($filter, $base) = #_;
my %filter = map { $_ => 1 } #$filter;
return grep { !$filter{$_} } #$base;
}
Alternatively,
my #filtered = filter_list(\#filter, #base);
could be implemented as
sub filter_list {
my $filter = shift;
my %filter = map { $_ => 1 } #$filter;
return grep { !$filter{$_} } #_;
}
What you're looking for is the difference of two sets. This, along with union, intersection, and a bunch of others are set operations. Rather than writing your own, there's plenty of modules for dealing with sets.
Set::Object is very fast and featureful. I'd avoid using the operator interface (ie. $set1 - $set2) as it makes the code confusing. Instead use explicit method calls.
use strict;
use warnings;
use v5.10;
use Set::Object qw(set);
my $set1 = set(1, 2, 3, 4, 5);
my $set2 = set(1, 2, 3, 4, 5, 6, 7);
say join ", ", $set2->difference($set1)->members;
Note that sets are unordered and cannot contain duplicates. This may or may not be what you want.
This uses List::Compare, a module with a large collection of routines for comparing lists.
Here you want get_complement
use warnings;
use strict;
use List::Compare;
my #arr1 = ( 1, 2, 3, 4, 5 );
my #arr2 = ( 1, 2, 3, 4, 5, 6, 7);
my $lc = List::Compare->new(\#arr1, \#arr2);
my #only_in_second = $lc->get_complement;
print "#only_in_second\n";
The module has many options.
If you don't need the result sorted, pass -u to the constructor for faster operation.
There is also the "Accelerated Mode", obtained by passing -a. For the purpose of efficient repeated comparisons between the same arrays many things are precomputed at construction. With this flag that is suppressed, which speeds up single comparisons. See List::Compare Modes.
These two options can be combined, List::Compare->new('-u', '-a', \#a1, \#a2).
Operations on three or more lists are supported.
There is also the functional interface, as a separate List::Compare::Functional module.

How can I tie an array slice to the original array so all changes that are made to one are made to both?

I need to be able to tie an array slice to the original array in such a way that any changes made to the original array (including removing elements) will also be made to the array slice. Is there a way to do this?
The following example does not work how I want it to, but it is simply there to demonstrate the point I am trying to make.
Example:
my #array = 1 .. 10;
my #slice = #array[3 .. 8];
splice #array, 5, 2;
print "ARRAY: ";
print join ', ', #array;
print "\n";
print "SLICE: ";
print join ', ', #slice;
Output:
ARRAY: 1, 2, 3, 4, 5, 8, 9, 10
SLICE: 4, 5, 6, 7, 8, 9
What I am looking for is a way to tie the slice to the original array so the output would look like this instead:
ARRAY: 1, 2, 3, 4, 5, 8, 9, 10
SLICE: 4, 5, 8, 9
Removing 6 and 7 from the original array would also remove it from the array slice.
How can I achieve something like this?
As has been said, that's a tall order. The short answer is no. A slice creates a copy of the elements.
Perl does have a Tie feature that might be just the trick.
perltie - how to hide an object class in a simple variable
So it fundamentally changes what a variable is behind the scenes. A whole world of possibilities opens up, and your scenario just might be in there.
perltie on perldoc.perl.org
Updated post as requested using Object oriented method. Maintained original response after <========> Line
Here's the object oriented approach as mentioned in comments.
Sample.pm
package Sample;
use strict;
use warnings;
use Exporter qw(import);
use List::MoreUtils qw(first_index);
our #y = qw (3 4 5 6 7 8 9); # Add your method of acquiring #y value here
our #EXPORT = qw (SpliceV2 #y);
## Your Splice Version 2
sub SpliceV2(#) {
my ($position,$length,#x) = #_;
for (my $i=1;$i<=$length;$i++) {
my $removeditem = $x[$position];
my $remove = first_index { $_ eq $removeditem } #y;
splice #x, $position, 1;
splice #y, $remove, 1;
}
return #x;
}
1;
Main script:
#!/usr/bin/perl
use Sample;
my #x = qw(1 2 3 4 5 6 7 8 9 10);
#x = SpliceV2(4,2,#x);
print "X: #x\n";
print "Y: #y\n";
Original post below
<==========>
Assuming the items you are removing are unique like if you are basing it on primary keys of a database, then you can use first_index from List::MoreUtils;
Here's a sample.
use List::MoreUtils qw(first_index);
my #x = qw (1 2 3 4 5 6 7 8 9 10);
my #y = qw (4 5 6 7 8 9);
## Let's say you want to remove 2 items after the 5th index
my $position = 5;
my $length = 2;
## Get the value of items to remove first
for (my $i=1;$i<=$length;$i++) {
my $removeditem = $x[$position];
my $remove = first_index { $_ eq $removeditem } #y;
splice #x, $position, 1;
splice #y, $remove, 1;
}
print "Array X\n";
print "$_," foreach(#x);
print "\nArray Y\n";
print "$_," foreach(#y);
print "\n";
You should get the result you wanted.
Array X
1,2,3,4,5,8,9,10,
Array Y
4,5,8,9,
use strict;
use warnings;
use Data::Dumper;
my #array = 1..10;
my #slice = \#array[3..8];
splice #array, 5, 2;
print "ARRAY: ";
print join ', ', #array;
print "\n";
print "SLICE: ";
print join ', ', #slice;
Output:
ARRAY: 1, 2, 3, 4, 5, 8, 9, 10
SLICE: SCALAR(0x29dcef0), SCALAR(0x29dcf20), SCALAR(0x29dcf08), SCALAR(0x29dcfb0), SCALAR(0x29dcfc, SCALAR(0x29dd058)
Instead removing the items you could assign a zero value to the item (it will assign a zero value in the #slice too, because #slice holds a refference to #array) and then remove the 0's from you #slice and voila, you have your updated #slice with your deleted elements

Passing arrays to a subroutine that prints each array separately

I know that this is probably a simple fix, but I have not been able to find the answer through google and searching through the questions here.
My goal is to pass multiple arrays to a subroutine that simply iterates through each array separately and prints each array with something before and after it.
What I have:
#A1 = (1, 2, 3);
#A2 = (4, 5, 6);
printdata(#A1, #A2) ;
sub printdata {
foreach(#_) {
print "$_" ;
print "###"
}
}
What I am attempting to get is:
123###456###
Instead its treating both arrays as one and iterating through each variable in the array, Putting the separator after every variable vice the entire array.
1###2###3###etc.....
I am not sure how to get the subroutine to treat the arrays as separate rather than as one.
Any Help would be greatly appreciated!
You need to pass the arrays as references:
#A1 = (1, 2, 3);
#A2 = (4, 5, 6);
printdata(\#A1, \#A2) ;
sub printdata {
foreach(#_) {
print #$_ ;
print "###"
}
}
The sub call expands the arrays into a list of scalars, which is then passed to the sub within the #_ variable. E.g.:
printdata(#A1, #A2);
is equal to:
printdata(1,2,3,4,5,6);
See the section on "Pass by Reference" in perldoc perlsub.
use strict;
use warnings;
use English qw<$LIST_SEPARATOR>;
my #A1 = (1, 2, 3);
my #A2 = (4, 5, 6);
{ local $LIST_SEPARATOR = '';
my #a = map { "#$_" } \#A1, \#A2;
$LIST_SEPARATOR = '###';
print "#a\n";
}
You also could have used join (po-tay-to, po-tah-to).
my #a = map { join( '', #$_ ) } \#A1, \#A2;
print join( '###', #a ), "\n";

Resources