Perl Array Order - arrays

I'am new with perl and stucked with the following exercise.
I have a multi-array and want order its element to descending to the inside arrays sum's.
I want to make the order with Schwartzian transform.
This is my vector:
my #vectors = ( [1], [ 1, 2, 3 ], [4], [ 2, 2, 1 ] );
This is the expected vector:
#sorted_vectors = ( [1,2,3], [2,2,1], [4], [1] );
So far I am tried with these:
(1)
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my #vectors = ( [1], [ 1, 2, 3 ], [4], [ 2, 2, 1 ] );
my #sorted_vectors;
# #sorted_vectors = ( [1,2,3], [2,2,1], [4], [1] );
my %hash=();
for(my $i=0;$i< scalar #vectors;$i++){
$hash{$i}=#vectors[$i];
}
for my $key ( sort { $hash{$b}[1] <=> $hash{$a}[1] } keys %hash ) {
push(#sorted_vectors,[#{$hash{$key}}]);
}
print Dumper( \#sorted_vectors );
(2)
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my #vectors = ( [1], [ 1, 2, 3 ], [4], [ 2, 2, 1 ] );
my #sorted_vectors;
# #sorted_vectors = ( [1,2,3], [2,2,1], [4], [1] );
my #sorted = map { $_->[0] }
sort { $a->[1] cmp $b->[1] }
map { [$_, foo($_)] }
#vectors;
sub foo{
my $res = 0;
foreach my $x (#_) {
$res+= $x;
}
return $res;
}
print Dumper(\#sorted);

Your code works with few alterations,
my #sorted = map { $_->[0] }
# sort { $a->[1] cmp $b->[1] } # string sort, ascending order
sort { $b->[1] <=> $a->[1] } # numeric sort, descending order
# map { [$_, foo($_)] } # foo() expects list, not array reference
map { [$_, foo(#$_)] } # dereferencing $_ aref for foo()
#vectors;
or same thing but using sum() from core module List::Util
use List::Util 'sum';
my #vectors = ( [1], [ 1, 2, 3 ], [4], [ 2, 2, 1 ] );
my #sorted_vectors = map $_->[0],
sort { $b->[1] <=> $a->[1] }
map [ $_, sum(#$_) ],
#vectors;
use Data::Dumper; print Dumper \#sorted_vectors;
output
$VAR1 = [
[
1,
2,
3
],
[
2,
2,
1
],
[
4
],
[
1
]
];

This is simple with List::UtilsBy::rev_nsort_by and List::Util::sum:
my #vectors = ( [1], [ 1, 2, 3 ], [4], [ 2, 2, 1 ] );
my #sorted = rev_nsort_by { sum #$_ } #vectors;
This sorts the list of #vectors in reverse numerical order (i.e. largest number first) of the result of its control block. The control block here simply sums all the individual elements of each component.

Related

Using join to concatenate array values

I have array values that is getting returned from SQL object.
my #keys = $db_obj->SelectAllArrayRef($sql);
print Dumper #keys;
gives
$VAR1 = [ [ '8853' ], [ '15141' ] ];
I need to create string from this array: 8853, 15141.
my $inVal = join(',', map { $_->[0] }, #$keys);
my $inVal;
foreach my $result (#$keys){
$inVal .= $result->[0];
}
my $inVal = join(',', #$keys);
Value i get is ARRAY(0x5265498),ARRAY(0x52654e0). I think its reference to the array. Any idea what am I missing here?
Don't pass arrays to Dumper; it leads to confusing output. $VAR1 is not a dump of #keys, it's a dump of $keys[0]. Instead, you should have done
print(Dumper(\#keys));
This would have given
$VAR1 = [ [ [ '8853' ], [ '15141' ] ] ];
The code you want is
join ',', map { $_->[0] }, #{ $keys[0] };
That said, it appears that ->SelectAllArrayRef returns a reference to the result, and so it should be called as follows:
my $keys = $db_obj->SelectAllArrayRef($sql);
For this,
print(Dumper($keys));
outputs
$VAR1 = [ [ '8853' ], [ '15141' ] ];
And you may use either of the methods you used in your question.
join ',', map { $_->[0] }, #$keys;
The first version should work for you:
my $arr = [ [ '8853' ], [ '15141' ] ];
my $values = join(',', map { $_->[0] } #$arr);
print $values . "\n";
8853,15141

Perl 2d Array Push

Why is there a difference in the creation of the following arrays #test1 and #test2?
#!/bin/perl -w
use Data::Dumper;
use warnings;
use strict;
my #test1 = [
['note', 1],
['note', 3]
];
print Dumper(#test1);
my #test2;
push(#test2, ['note', 1]);
push(#test2, ['note', 3]);
print Dumper(#test2);
Datadump for test1:
$VAR1 = [
[
'note',
1
],
[
'note',
3
]
];
Dumpt for test2:
$VAR1 = [
'note',
1
];
$VAR2 = [
'note',
3
];
Is there a possibility to create the same result of #test1 with iterative pushing to #test2?
Instead of:
my #test1 = [
['note', 1],
['note', 3]
];
You probably want:
my #test1 = (
['note', 1],
['note', 3]
);
The square brackets will create an anonymous array and will return a reference to the new array. So #test1 will contain a single scalar value which is a reference to an array.
Also when dumping a structure like an array, it is often clearer to prefix it with a backslash in order to pass a reference:
print Dumper(\#test2);
Which gives:
$VAR1 = [
[
'note',
1
],
[
'note',
3
]
];
Remember when you pass an array in a Perl function call, the array gets "flattened" into the argument list.

Number of connections in a hash of arrays

I have a hash of arrays like the following:
my %HoA = (
"M" => [ "L", "E" ],
"L" => [ "I" ],
"E" => [ "B", "C" ],
"B" => [ "A" ],
"C" => [ "A" ]
);
You can visualize it in this way:
M
/ \
L E
/ / \
I B C
\ /
A
Now, I would like to know the number of connections for every node:
M 6
E 3
L 1
B 1
C 1
I 0
A 0
With the igraph package in R this is straightforward but I am struggling to do the same with a hash of arrays in Perl.
You can build graphs in Perl using the Graph module
You need a Graph::Directed object
Here's an example
use strict;
use warnings 'all';
use Graph::Directed;
my %HoA = (
"M" => [ "L", "E" ],
"L" => [ "I" ],
"E" => [ "B", "C" ],
"B" => [ "A" ],
"C" => [ "A" ]
);
# Build the graph
#
my $g = Graph::Directed->new;
while ( my ($from, $to) = each %HoA ) {
$g->add_edge($from, $_) for #$to;
}
# Build a table of successors of each vertex
#
my %succ;
for my $v ( $g->vertices ) {
my #succ = $g->all_successors($v);
$succ{$v} = \#succ;
}
# Print the vertices in descending order of successors
#
for my $v ( sort { #{$succ{$b}} <=> #{$succ{$a}} } $g->vertices ) {
printf "%s %d\n", $v, scalar #{$succ{$v}};
}
output
M 6
E 3
C 1
B 1
L 1
A 0
I 0

access / print nth element of sub array, for every array

I have a multidimensional array:
#multarray = ( [ "one", "two", "three" ],
[ 4, 5, 6, ],
[ "alpha", "beta", "gamma" ]
);
I can access #multarray[0]
[
[0] [
[0] "one"
[1] "two"
[2] "three"
]
]
or even #multarray[0][0]
"one"
But how to I access say the 1st sub element of every sub array? something akin to multarray[*][0] so produce:
"one"
4
"alpha"
Thanks!
You can use map and dereference each array:
use warnings;
use strict;
use Data::Dumper;
my #multarray = (
[ "one", "two", "three" ],
[ 4, 5, 6, ],
[ "alpha", "beta", "gamma" ]
);
my #subs = map { $_->[0] } #multarray;
print Dumper(\#subs);
__END__
$VAR1 = [
'one',
4,
'alpha'
];
See also: perldsc
Using a for() loop, you can loop over the outer array, and use any of the inner elements. In this example, I've set $elem_num to 0, which is the first element. For each loop over the outer array, we take each element (which is an array reference), then, using the $elem_num variable, we print out the contents of the inner array's first element:
my $elem_num = 0;
for my $elem (#multarray){
print "$elem->[$elem_num]\n";
}

How can I delete duplicate values across arrays stored in hash?

I have the following hash:
my %HASH = (
'List1' => [ 'the', 'red', 'cat', 'jumps' ],
'List2' => [ 'the', 'brown', 'fox', 'jumps' ],
'List3' => [ 'a', 'red', 'fox', 'jumps' ],
);
I want to delete duplicate elements across these arrays, so that only unique elements remain. The desired output would be the following:
my %HASH = (
'List1' => [ 'cat' ],
'List2' => [ 'brown' ],
'List3' => [ 'a' ],
);
In other words, if an element is present in both List1 and List2, it should be deleted from both lists.
I have tried to do the following:
use strict;
use warnings;
use diagnostics;
use Data::Dumper;
foreach my $key ( keys %HASH ) {
foreach ( #{$HASH{$key}} ) {
if(exists($HASH{$key})){
#{$HASH{$key}} = delete($HASH{$key});
}
}
}
print Dumper(\%HASH);
Which doesn't seem to do anything, the hash remains the way it was. I'm still pretty new to Perl, so I'm not sure where I went wrong with that. But Perldoc says that calling exists on array values is deprecated anyway, so any solution which uses something other than exists is welcome too!
use strict;
use warnings;
my %hash = (
'List1' => [ 'the', 'red', 'cat', 'jumps' ],
'List2' => [ 'the', 'brown', 'fox', 'jumps' ],
'List3' => [ 'a', 'red', 'fox', 'jumps' ],
);
# first, we count all words
my %count;
for my $words (values %hash) {
for my $word (#$words) {
$count{$word}++;
}
}
# Now, we filter the words with `grep` so that only
# those remain which were found once
for my $words (values %hash) {
#$words = grep { $count{$_} == 1 } #$words;
}
use Data::Dump;
dd \%hash;
Outputs:
{ List1 => ["cat"], List2 => ["brown"], List3 => ["a"] }

Resources