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
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
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.
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
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";
}
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"] }