How can I find the connected components of a graph in Perl? - linux

I have the following collection of nodes and edges. What I want to do is to find all the distinct graph from it.
my %connections=(36=>[31],10=>[3,4],31=>[30,22],30=>[20],22=>[20,8],20=>[1],8=>[5],5=>[2],2=>[1,20], 3=>[7]);
In this example it will yield:
my %all_graph = {
graph1 => {36=>[31],31=>[30,22],30=>[20],22=>[20,8],20=>[1],8=>[5],5=>[2],2=>[1,20]}.
graph2 => {10=>[3,4], 3=>[7]}
};
Is there any existing algorithms that does that?

Use the Graph module:
#!/usr/bin/perl
use strict; use warnings;
use Graph;
my %connections = (
36 => [ 31 ],
10 => [ 3, 4],
31 => [ 30, 22],
30 => [ 20 ],
22 => [ 20, 8],
20 => [ 1 ],
8 => [ 5 ],
5 => [ 2 ],
2 => [ 1, 20 ],
3 => [ 7 ]
);
my $g = Graph->new( undirected => 1 );
for my $src ( keys %connections ) {
for my $tgt ( #{ $connections{$src} } ) {
$g->add_edge($src, $tgt);
}
}
my #subgraphs = $g->connected_components;
my #allgraphs;
for my $subgraph ( #subgraphs ) {
push #allgraphs, {};
for my $node ( #$subgraph ) {
if ( exists $connections{ $node } ) {
$allgraphs[-1]{$node} = [ #{ $connections{$node} } ];
}
}
}
use YAML; print Dump \#allgraphs;
Output:
[sinan#archardy SO]$ ./g
---
- 2:
- 1
- 20
20:
- 1
22:
- 20
- 8
30:
- 20
31:
- 30
- 22
36:
- 31
5:
- 2
8:
- 5
- 10:
- 3
- 4
3:
- 7

To find the connected components of an undirected graph you just do a BFS or DFS (Breadth/Depth first search).
Here some sample BFS code
my %connections=(36=>[31],10=>[3,4],31=>[30,22],30=>[20],22=>[20,8]
,20=>[1],8=>[5],5=>[2],2=>[1,20], 3=>[7]);
my $full_connections = {}; # Build a REAL graph with full 2-way edge lists
foreach my $node (keys %connections) {
foreach my $node2 (#{ $connections{$node} }) {
print "$node, $node2\n";
$full_connections->{$node}->{$node2} = 1;
$full_connections->{$node2}->{$node} = 1;
}
}
my %all_graph = ();
my $current_graph = 0;
my %visited = ();
my #to_visit = ();
foreach my $node (keys %$full_connections) {
next if exists $visited{$node};
# start the next segment
$current_graph++;
#to_visit=($node);
while (#to_visit) {
$node_to_visit = shift #to_visit;
#next if $visited{$node_to_visit};
$visited{$node_to_visit} = $current_graph;
push #to_visit, grep { !exists $visited{$_} }
keys %{ $full_connections->{$node_to_visit} };
}
}
# Now reconstruct %all_graph from %visited - left as exercise for the reader
print Data::Dumper->Dump([\%visited]);

I'd suggest the following algorithm:
1.) Move all nodes into a working set N.
2.) Starting with an arbitrary node perform a graph search (depth-first or breadth-first). Add all visited nodes and edges to the first subgraph, remove visited nodes from N
3.) If N is non-empty, select the next starting node and go to step 2.) for the next subgraph.

Related

How do I generate an array with macro! by applying a function to each element?

I want to generate a static array (doing this at runtime is not an option) with help of a macro.
My attempts are
macro_rules! test {
($($data:expr),*) => {
[ test!(#xform $($data),*) ]
};
(#xform) => { };
(#xform $a:expr) => { be32($a) };
(#xform $a:expr, $($data:expr),*) => { be32($a), test!(#xform $($data),*) };
}
// just for simplicity...
const fn be32(v: u32) -> u32 { v + 1 }
static FILE_HEADER_0: [u32;2] = test!(1, 2);
static FILE_HEADER_1: [u32;2] = [be32(1), be32(2)];
but this fails with
error: macro expansion ignores token `,` and any following
--> src/lib.rs:8:52
|
3 | [ test!(#xform $($data),*) ]
| ------------------------- help: you might be missing a semicolon here: `;`
| |
| caused by the macro expansion here
...
8 | (#xform $a:expr, $($data:expr),*) => { be32($a), test!(#xform $($data),*) };
| ^
|
= note: the usage of `test!` is likely invalid in expression context
I expect that FILE_HEADER_0 is generated like FILE_HEADER_1
Is this possible with normal macro_rules! or do I have to use proc_macro?
Macros cannot expand to multiple items. They have to generate only one. Your macro expands to multiple comma-separated expressions, and this is invalid.
The usual solution to that is push-down accumulation: accumulate the resulting array in the macro and keep adding elements to it:
macro_rules! test {
($($data:expr),*) => {
test!(#xform [] $($data),*)
};
(#xform $arr:tt) => { $arr };
(#xform [ $($arr:tt)* ] $a:expr) => {
test!(#xform [ $($arr)* be32($a) ])
};
(#xform [ $($arr:tt)* ] $a:expr, $($data:expr),*) => {
test!(#xform [ $($arr)* be32($a), ] $($data),*)
};
}
In the first expansion we will have test!(#xform [] 1, 2). This will reach the last arm and expand to test!(#xform [ be(1), ] 2). This will expand (by the third arm) to test!(#xform [ be(1), be(2) ]) which will finally expand to [be(1), be(2)] by the second arm.
In this case, however, you don't need complex solutions. A simple macro will do:
macro_rules! test {
($($data:expr),*) => {
[ $( be32($data) ),* ]
};
}

Can anyone explain how to print all keys if two keys are same but with different values?

In this code two keys "39" are same name but different values , I want to print both keys
use strict;
use warnings;
my %studentnames = (
14 => Martha,
27 =>Vivek,
31 =>Era,
16 =>Marty,
25 =>Jason,
29 =>Socrates,
19 =>Uri,
39 =>Nitin ,
39 =>Plato,
);
foreach my $name (sort keys %studentnames)
{
printf "%-8s %s\n", $name, $studentnames{$name};
}
I am getting error.
Bareword "Martha" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Vivek" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Era" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Marty" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Jason" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Socrates" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Uri" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Nitin" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Bareword "Plato" not allowed while "strict subs" in use at /home/9945b48d30946ed2641d9778b42cb182.pl line 10.
Expected Output
14 Martha
27 Vivek
31 Era
16 Marty
25 Jason
29 Socrates
19 Uri
39 Nitin
39 Plato
Can anyone tell me how to do it?
Two keys cannot be the same. One will overwrite the other. If you want to have multiple values for one key then you need to design your data structure to support that (e.g. by having the value be an arrayref).
Your error messages are unrelated to that problem (you forgot to put quotes around your string values).
This is kinda close:
use strict;
use warnings;
use Tie::Hash::MultiValueOrdered;
tie my %studentnames, 'Tie::Hash::MultiValueOrdered';
%studentnames = (
14 => 'Martha',
27 => 'Vivek',
31 => 'Era',
16 => 'Marty',
25 => 'Jason',
29 => 'Socrates',
19 => 'Uri',
39 => 'Nitin',
39 => 'Plato',
);
tied(%studentnames)->fetch_list;
while ( my ( $key, $value ) = each %studentnames ) {
print "$key => #$value\n";
}
But really you want to use a different data structure. Perhaps an array of arrayrefs?
use strict;
use warnings;
my #students = (
[ 14 => 'Martha' ],
[ 27 => 'Vivek' ],
[ 31 => 'Era' ],
[ 16 => 'Marty' ],
[ 25 => 'Jason' ],
[ 29 => 'Socrates' ],
[ 19 => 'Uri' ],
[ 39 => 'Nitin' ],
[ 39 => 'Plato' ],
);
for my $student ( #students ) {
my ( $num, $name ) = #$student;
print "$num => $name\n";
}
Or an array of hashrefs:
use strict;
use warnings;
my #students = (
{ num => 14 , name => 'Martha' },
{ num => 27 , name => 'Vivek' },
{ num => 31 , name => 'Era' },
{ num => 16 , name => 'Marty' },
{ num => 25 , name => 'Jason' },
{ num => 29 , name => 'Socrates' },
{ num => 19 , name => 'Uri' },
{ num => 39 , name => 'Nitin' },
{ num => 39 , name => 'Plato' },
);
for my $student ( #students ) {
print "$student->{num} => $student->{name}\n";
}
Or a hash of arrayrefs:
use strict;
use warnings;
my %students = (
14 => [ 'Martha' ],
27 => [ 'Vivek' ],
31 => [ 'Era' ],
16 => [ 'Marty' ],
25 => [ 'Jason' ],
29 => [ 'Socrates' ],
19 => [ 'Uri' ],
39 => [ 'Nitin', 'Plato' ],
);
for my $key ( sort keys %students ) {
for my $name ( #{$students{$key}} ) {
print "$key => $name\n";
}
}
Or you could even create a lightweight "person" class.
use Z;
my $Person = class sub {
has num => ( type => PositiveInt );
has name => ( type => NonEmptyStr );
};
my #students = (
$Person->new( num => 14, name => 'Marta' ),
$Person->new( num => 27, name => 'Vivek' ),
$Person->new( num => 31, name => 'Era' ),
$Person->new( num => 16, name => 'Marty' ),
$Person->new( num => 25, name => 'Jason' ),
$Person->new( num => 29, name => 'Socrates' ),
$Person->new( num => 19, name => 'Uri' ),
$Person->new( num => 39, name => 'Nitin' ),
$Person->new( num => 39, name => 'Plato' ),
);
for my $student ( #students ) {
printf "%s => %s\n", $student->num, $student->name;
}
There's a lot of ways to go about solving this, but a single flat hash of strings is probably not one of them.
As a starter: the hash values are strings, so they need to be quoted. This is why you are getting a syntax error:
my %studentnames = (
14 => 'Martha',
27 => 'Vivek',
31 => 'Era',
...
);
Then: there is a misconception of what a Perl hash is. Each key in the hash must be unique. Perl tolerates declaring a hash with duplicate keys, but under the hood, only the last value of each key is retained.
So this:
my %studentnames = (
14 => 'Martha',
39 => 'Nitin',
39 => 'Plato'
);
Is equivalent to:
my %studentnames = (
14 => 'Martha',
39 => 'Plato'
);
Another way to see it is to put the assignments in separate instructions:
my %studentnames;
$studentnames{14} = 'Martha';
$studentnames{39} = 'Nitin';
$studentnames{39} = 'Plato';
print $studentnames{39}, "\n";
# Plato
In addition to excellent answers provided by others, and explanations of how to make the code work (e.g., quote the names, etc), here is another simple solution. I assume that the student names are unique (as they indeed appear in your example). In this case, use the reverse of your hash. That is, change it from the mapping of number to name to the mapping of name to number. Sort the hash by values (student numbers) numerically, then by keys (student names) ASCIIbetically. This is my guess as to one possible way of sorting that makes intuitive sense to the user.
#!/usr/bin/env perl
use strict;
use warnings;
my %student_name_to_num =
reverse (
14 => 'Martha',
27 => 'Vivek',
31 => 'Era',
16 => 'Marty',
25 => 'Jason',
29 => 'Socrates',
19 => 'Uri',
39 => 'Nitin',
39 => 'Plato',
);
foreach my $name ( sort {
$student_name_to_num{$a} <=> $student_name_to_num{$b} ||
$a cmp $b
} keys %student_name_to_num ) {
printf "%-8s %s\n", $student_name_to_num{$name}, $name;
}
Output:
14 Martha
16 Marty
19 Uri
25 Jason
27 Vivek
29 Socrates
31 Era
39 Nitin
39 Plato
Note that the order of the records is different from the sorted order you showed. But it is not clear how you want the records to be sorted (see also my comment under the question).

Terraform interpolation for creating a map

I'm struggling with this interpolation, I have variables below like.
primary = ["foo.dev","all.bar.com"]
secondary = ["module.foo.dev","*.foo.dev","all.bar.com"]
I want my output to be
{
"foo.dev" = ["module.foo.dev","*foo.dev"]
"all.bar.com" = ["all.bar.com"]
}
Using Terraform 0.12.20
I tried in terraform console , I'm not able to achieve my desired output. Is there any easy solution?
[for i in ["foo.dev","all.bar.com"]: map(i,[for j in ["module.foo.dev","*foo.dev","all.bar.com"]: replace(j,i,"")!=j == true ? j : 0])]
[
{
"foo.dev" = [
"module.foo.dev",
"*foo.dev",
"0",
]
},
{
"all.bar.com" = [
"0",
"0",
"all.bar.com",
]
},
]
You can do this with the following expression:
{ for i in ["foo.dev", "all.bar.com"] : i => [for j in ["module.foo.dev", "*foo.dev", "all.bar.com"] : j if can(regex(i, j))] }
This results in the following value:
{
"all.bar.com" = [
"all.bar.com",
]
"foo.dev" = [
"module.foo.dev",
"*foo.dev",
]
}
which is exactly what you requested.
The iterative explanation follows:
First, specify the type is a map:
{}
Now, we should iterate over the elements in the first list:
{ for i in ["foo.dev", "all.bar.com"] : }
Next, we assign the i value to the key of the map, and initialize a list for the value:
{ for i in ["foo.dev", "all.bar.com"] : i => [] }
However, we need to assign the values from the elements in the second list to the list for the key of the resulting map (the below example is sub-optimal because we do not need a for expression, but it is illustrative for the next step):
{ for i in ["foo.dev", "all.bar.com"] : i => [for j in ["module.foo.dev", "*foo.dev", "all.bar.com"] : j] }
Finally, we want to filter the elements in the list value based on the key. We can use the can function outside the regex function to return a boolean based on whether there is a match. We use this for the conditional on whether the element of the second list should be added to the list in the value:
{ for i in ["foo.dev", "all.bar.com"] : i => [for j in ["module.foo.dev", "*foo.dev", "all.bar.com"] : j if can(regex(i, j))] }
and we have arrived at the solution.

Is there a standard functional name for this function?

I'm sure this function is likely common ( or possibly achievable other ways ) but I'm not sure of what it'd be called. I'm thinking of a sliding window of a certain size :-
let slidingMap = (arr,size, f) => {
r = []
arr.reduce((acc, n) => {
let b = acc.concat(n);
if(b.length > size) {
b.shift();
}
if(b.length == size) {
r.push(f(b))
}
return b;
},[])
return r;
}
so given slidingMap([1,2,3,4,5,6], 2, b => b)
you'd get [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ], [ 5, 6 ] ]
and slidingMap([1,2,3,4,5,6], 3, b => b)
you'd get [ [ 1, 2, 3 ], [ 2, 3, 4 ], [ 3, 4, 5 ], [ 4, 5, 6 ] ]
or for calculating differences :-
slidingMap([1,2,3,7,5,6],2, b => b.reduceRight((a, n) => a? a-n : n))
you'd get [ 1, 1, 4, -2, 1 ]
or moving average :-
slidingMap([1,2,3,7,5,6],3, b => b.reduce((a, n) => a+n,0)/b.length)
you'd get [ 2, 4, 5, 6 ]
so, is there a commonly implemented function(s) that achieves this?
Update
Probablly better implemented as
let sliding = (arr,size) => {
r = []
arr.reduce((acc, n) => {
let b = acc.concat(n);
if(b.length > size) {
b.shift();
}
if(b.length == size) {
r.push(b)
}
return b;
},[])
return r;
}
then just use map
sliding([1,2,3,4,5],2).map(somefunc);
Or perhaps using zip and skip ( using lodash in this case )
let sliding = (arr, size) =>
_.zip(..._.range(size).map(i => arr.slice(i)))
.filter(a => !a.some(v => v == undefined))
only trick here is the zip will insert undefined when it has no match so they need to be filtered out.

node_redis get zrange withscores

Does anybody know how can I get members with scores by node redis?
I tried something like this:
client.ZRANGE(key, 0, -1, withscores, function(err, replies) {
});
Thanks.
This code looks good. Check out the following link for retrieving what you want :
http://ricochen.wordpress.com/2012/02/28/example-sorted-set-functions-with-node-js-redis/
Added the code here from that link example in case it is ever removed.
var rc=require('redis').createClient();
var _=require('underscore');
rc.zincrby('myset', 1, 'usera');
rc.zincrby('myset', 5, 'userb');
rc.zincrby('myset', 3, 'userc');
rc.zrevrange('myset', 0, -1, 'withscores', function(err, members) {
// the resulting members would be something like
// ['userb', '5', 'userc', '3', 'usera', '1']
// use the following trick to convert to
// [ [ 'userb', '5' ], [ 'userc', '3' ], [ 'usera', '1' ] ]
// learned the trick from
// http://stackoverflow.com/questions/8566667/split-javascript-array-in-chunks-using-underscore-js
var lists=_.groupBy(members, function(a,b) {
return Math.floor(b/2);
});
console.log( _.toArray(lists) );
});
rc.quit();
Seems your code is right. The following is the syntax to get zrange.
without score:
redisClient.zrange(keyName,start,stop,function(err,result){
//result is array
// every index will give you member name
})
Ex :
redisClient.zrange("mySortedset",-1,-1,function(err,result){
//result is array
// every index will give you member name
})
with score:
redisClient.zrange(keyName,start,stop,'withscores',function(err,result){
//result is array
// here even index will hold member
// odd index will hold its score
})
Ex :
redisClient.zrange("mySortedset",-1,-1,'withscores',function(err,result){
//result is array
// here even index will hold member
// odd index will hold its score
})
I tried with the prior accepted answers but i could not get the result i want and later i tried with the following code and got appropriate result,
Original output:
[ 'player:522',
'19685',
'player:164',
'19594',
'player:807',
'19171',
'player:694',
'19165',
'player:905',
'19108',
'player:859',
'19087',
'player:432',
'18973',
'player:515',
'18831',
'player:163',
'18750',
'player:4',
'18552' ]
Expected output:
{
"player:522": "19685",
"player:164": "19594",
"player:807": "19171",
"player:694": "19165",
"player:905": "19108",
"player:859": "19087",
"player:432": "18973",
"player:515": "18831",
"player:163": "18750",
"player:4": "18552"
}
Solution:
redisClient.ZREVRANGE('daily', 1, 10, 'WITHSCORES', function(err, result) {
result = _.fromPairs(_.chunk(result, 2));
return res.status(200).json(result);
});
The right approach for versions ^2.0,
var args = [ key,to, from ];
redisClient.zrevrangebyscore(args,function(err,data){
Vanilla JS Solution
Redis call:
redisClient.zrange(keyName, start, stop, 'withscores', function(err, result) {
// result.reduce ... (See below)
}
Here is a Vanilla-JS solution that I came up with pretty quickly.
For me, personally, it does not make sense to import underscore or any other library to perform such an easy task:
result.reduce(function (a, c, i) {
var idx = i / 2 | 0;
if (i % 2) {
a[idx].score = c;
} else {
a[idx] = { id: c };
}
return a;
}, []);
Assuming this input:
['player1', 13, 'player2', 11, 'player4', 7, 'player3', 3, 'player5', 0]
This function yields:
[
{ id: 'player1', score: 13 },
{ id: 'player2', score: 11 },
{ id: 'player4', score: 7 },
{ id: 'player3', score: 3 },
{ id: 'player5', score: 0 }
]
Here is another one to transform the result into a two-dimensional array:
result.reduce(function (a, c, i) {
var idx = i / 2 | 0;
if (i % 2) {
a[idx].push(c);
} else {
a[idx] = [c];
}
return a;
}, []);
which produces the following array:
[
[ 'player1', 13 ],
[ 'player2', 11 ],
[ 'player4', 7 ],
[ 'player3', 3 ],
[ 'player5', 0 ]
]
One line lambda version:
result.reduce((a, c, i) => i % 2 ? (a[i / 2 | 0].data = c, a) : (a[i / 2 | 0] = { id: c }, a), []);
Vanilla JS reduce works well here.
const result = [
'player:522',
'19685',
'player:164',
'19594',
'player:807',
'19171',
'player:694',
'19165',
'player:905',
'19108',
'player:859',
'19087',
'player:432',
'18973',
'player:515',
'18831',
'player:163',
'18750',
'player:4',
'18552'
]
const map = result.reduce((map, k, i, res) => {
if (i % 2 !== 0) {
map[res[i - 1]] = Number(k);
}
return map;
}, {})
map is now:
{
'player:522': 19685,
'player:164': 19594,
'player:807': 19171,
'player:694': 19165,
'player:905': 19108,
'player:859': 19087,
'player:432': 18973,
'player:515': 18831,
'player:163': 18750.65468,
'player:4': 18552
}
var data = []
results.map((result, index) => {
if (index % 2 == 0) {
data.push(results[index] = { player: results[index], score: results[index+1] })
}
})
console.log(data)

Resources