Number of connections in a hash of arrays - 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

Related

Iterating over arrays simultaneously using jq

I'm describing the output I'd like to have using jq
Here my json file
{
"partitions": [
{
"replicas": [
0,
1,
2
],
"log_dirs": [
"any",
"any",
"any"
]
},
{
"replicas": [
2,
0,
1
],
"log_dirs": [
"any",
"any",
"any"
]
},
[...]
I would like, for every object in partitions[] to replace the value of the i th string in log_dirs[] by its concatenation with the i th number in replicas[] in order to have something like this
{
"partitions": [
{
"replicas": [
0,
1,
2
],
"log_dirs": [
"any0",
"any1",
"any2"
]
},
{
"replicas": [
2,
0,
1
],
"log_dirs": [
"any2",
"any0",
"any1"
]
},
[...]
Use a reduce loop with a range
.partitions[] |= reduce range(0; ( .replicas | length ) ) as $r
( . ; .log_dirs[$r] += ( .replicas[$r] | tostring ) )
The reduce expression works by iterating over the entire log_dirs array upto the length of replicas list and modifying each entry .log_dirs[$r] (here r runs from 0 - length of replicas) by appending the corresponding value at replicas[$r]. Since replicas contains numerics, it needs to be converted to string for the append operation.
jqplay - demo
I'm not quite happy with this, but it does work:
jq '.partitions[] |= . + (
. as $p | {
log_dirs: [
range(.replicas | length) |
"\($p.log_dirs[.])\($p.replicas[.])"
]
}
)' in.json

Create JSON from two arrays

I have two arrays say: x=[1,2,3] and y=['a', 'b', 'c']. I want to create a json using x and y arrays in rails console. What is the optimized way to do it.
The desired JSON should looks like this:
{
"obj":
[
{
"key":"a",
"value": 1
},
{
"key":"b",
"value": 2
},
{
"key":"c",
"value": 3
}
]
}
x = [1,2,3] and y = ['a', 'b', 'c']
{obj: y.zip(x).map { |k, v| {key: k, value: v} } }
#⇒ {
# :obj => [
# {
# :key => "a",
# :value => 1
# },
# {
# :key => "b",
# :value => 2
# },
# {
# :key => "c",
# :value => 3
# }
# ]
# }
If you insist on having string keys:
{ 'obj' => y.zip(x).map { |k, v| { 'key' => k, 'value' => v } } }
To get a json out of the hash, just call to_json on it.

Perl merge hash of array

I have hash of array of numbers,
I would like to merge the hash elements with common numbers.
eg.
Input Hash
%HoA = (
A1 => [ "1", "2", "3", "4" ],
A2 => [ "5", "6", "7", "8" ],
A3 => [ "1", "9", "10", "11" ],
A4 => [ "12", "13", "14", "10" ],
);
Output Hash
%HoA_output = (
A1 => [ "1", "2", "3", "4", "9", "10", "11", "12", "13", "14" ],
A2 => [ "5", "6", "7", "8" ],
);
I need a solution that could quickly evaluate a hash that has nearly 50k keys with 8 numbers in each array.
regards
This is essentially a graph problem where you want to determine the sets of unconnected components
This solution uses the Graph::Undirected::Components module, whose sole purpose is to do exactly that. Hopefully it will be fast enough for your extended data set, but it is far easier for you to determine that than for me
The program creates a graph, and adds edges (connections) from every key in the data to each element of its value. Then, calling connected_components returns all the distinct sets of nodes — both keys and values — that are connected to one another
The final for loop filters the keys from the values once more using part from List::MoreUtils, based on whether the node value appears as a key in the original hash data. (You will have to adjust this if any of the key values can also appear in the values.) Then the first of the keys together with the sorted value items are used to create a new element in the %result hash
use strict;
use warnings;
use Graph::Undirected::Components;
use List::Util 'minstr';
use List::MoreUtils 'part';
my %data = (
A1 => [ 1, 2, 3, 4 ],
A2 => [ 5, 6, 7, 8 ],
A3 => [ 1, 9, 10, 11 ],
A4 => [ 12, 13, 14, 10 ],
);
my $graph = Graph::Undirected::Components->new;
while ( my ($k, $v) = each %data ) {
$graph->add_edge($k, $_) for #$v;
}
my %result;
for my $component ( $graph->connected_components ) {
my #keys_vals = part { $data{$_} ? 0 : 1 } #$component;
my $key = minstr #{ $keys_vals[0] };
my #values = sort { $a <=> $b } #{ $keys_vals[1] };
$result{$key} = \#values;
}
use Data::Dump;
dd \%result;
output
{ A1 => [1 .. 4, 9 .. 14], A2 => [5 .. 8] }
OK, pretty fundamentally - this isn't an easy one, because you do need to check each element against each other to see if they're present. The best I can come up with is saving some effort by merging lists as you go, and using an index to track dupes.
I would approach it like this:
use strict;
use warnings;
use Data::Dumper;
my %HoA = (
A1 => [ "1", "2", "3", "4" ],
A2 => [ "5", "6", "7", "8" ],
A3 => [ "1", "9", "10", "11" ],
A4 => [ "12", "13", "14", "10" ],
);
print Dumper \%HoA;
my %found;
sub merge_and_delete {
my ( $first_key, $second_key ) = #_;
print "Merging $first_key with $second_key\n";
#use hash to remove dupes.
my %elements;
foreach my $element ( #{ $HoA{$first_key} }, #{ $HoA{$second_key} } )
{
$elements{$element}++;
#update index - don't want to point it to an array we're deleting
$found{$element} = $first_key;
}
#sorting for neatness - you might want to do a numeric sort instead,
#as by default %HoA contains text elements.
$HoA{$first_key} = [ sort keys %elements ];
delete $HoA{$second_key};
}
foreach my $key ( sort keys %HoA ) {
print "$key\n";
foreach my $element ( sort #{ $HoA{$key} } ) {
if ( $found{$element} ) {
#this element is present in another list, we merge.
print "$element found in $found{$element}\n";
merge_and_delete( $found{$element}, $key );
last;
}
else {
#add this unique match to our index
print "$element -> $key\n";
$found{$element} = $key;
}
}
}
print Dumper \%HoA;
You iterate each of the element on %HoA, and make an index table %found. This index table you use to detect if an element has already been seen, and then trigger a merge - and then rebuilt the index. You may need to watch for memory consumption on a large data set though, because your index can grow to be nearly as large as your original data set (if enough unique elements are present).
But because we stop processing on the first match, we don't need to check every key any more, and because we discard the merged array and update the index, we don't need to do an all-to-all comparison any more.

Perl Array Order

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.

PANIC=runtime error: index out of range

I have the following code, but i can't figure out why the error:PANIC=runtime error: index out of range is happening
type Board struct {
Tboard [9]string
Player1 Player
Player2 Player
}
func makeBoard() *Board {
b := &Board{Tboard: [9]string{}}
for x := 0; x < len(b.Tboard); x++ {
b.Tboard[x] = E // E = "[ ]"
fmt.Println(b.Tboard[x])
}
fmt.Println(len(b.Tboard)) // => 9
fmt.Print("Error: ")
fmt.Println(b) // => Error: %!v(PANIC=runtime error: index out of range)
fmt.Println(b.Tboard) // => [[ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]]
return b
}

Resources