I want to find common elements in Arrays listed in a hash (Hash of arrays) in iterative manner and then divide each "intersection" by scalar value of first array. Iteration of intersection with other arrays for each array.
my #CS1= ("c1", "c2", "c3", "c4", "-c5");
my #CS2= ("c1", "c2", "c8", "c9");
my #CS3= ("c1", "c2", "c3");
my %CSHash= ( "set1" => [#CS1],
"set2"=> [#CS2],
"set3" => [#CS3],
);
My proposed Solution: But, it does not generate desired out put.
my %union=();
my %isect=();
my $cumLativeIsect=0;
foreach my $lst(keys %CSHash)
{
my $elCount=0;
foreach my $ele(#{$CSHash{$lst}})
{
$elCount++;
$union{$ele}++ && $isect{$ele}++;
}
my #intrsection= keys %isect;
if($elCount!=0 && scalar #intrsection!=0 )
{
$cumLativeIsect+= scalar #intrsection/$elCount;
}
}
Mathemetically, I am loking for following computation ( intr=intersection):
Intrsection=|{(cs1 intr cs1)/cs1+ (cs1 intr cs2)/cs1+ (cs1 intr cs3)/cs1}|+|{(cs2 intr cs2)/cs2+ (cs2 intr cs1)/cs2+ (cs2 intr cs3)/cs2}|+|{(cs3 intr cs1)/cs1+ (cs3 intr cs2)/cs1+ (cs3 intr cs3)/cs3}|
Here is a suggestion. I have renamed some of your variables, and used an array of array instead of a hash of arrays. After our discussion in the comments, I assumed you wanted to compute the following:
{|cs1 ∩ cs1|/|cs1| + |cs1 ∩ cs2|/|cs1| + |cs1 ∩ cs3|/|cs1| + ... }
+ {|cs2 ∩ cs1|/|cs2| + |cs2 ∩ cs2|/|cs2| + |cs2 ∩ cs3|/|cs2| + ... }
+ ...
Here is the code:
use strict;
use warnings;
use List::Util qw(any);
my #sets = (
[ "c1", "c2", "c3", "c4", "-c5"],
[ "c1", "c2", "c8", "c9"],
[ "c1", "c2", "c3"],
[ "c1", "c2", "c3"],
[ ],
[ ],
);
my $intr_sect = 0;
for my $set1 ( #sets ) {
my $N = scalar #$set1;
for my $set2 ( #sets ) {
my #intersect;
for my $item ( #$set2 ) {
if ( any { $_ eq $item } #$set1 ) {
push #intersect, $item;
}
}
$intr_sect += (scalar #intersect) / $N if $N;
}
}
Related
my #CLASS_TYPES = ("INTRA", "BB", "CAT");
my #INTRA_NEIGH = ("1.1.1.1/32","2.2.2.2/32");
my #BB_NEIGH = ("3.3.3.3/32","4.4.4.4/32" );
foreach my $class (#CLASS_TYPES) {
my $csv = #.$class._NEIGH;
print($csv);
when I print $csv I am expecting to print the array values how do I achieve that
That's not the right approach. See Why it's stupid to `use a variable as a variable name' and A More Direct Explanation of the Problem.
The very fact that #CAT_NEIGH doesn't exist illustrates part of the problem.
Solution:
my #CLASS_TYPES = ("INTRA", "BB", "CAT");
my %NEIGH = (
INTRA => [ "1.1.1.1/32", "2.2.2.2/32" ],
BB => [ "3.3.3.3/32", "4.4.4.4/32" ],
);
for my $class (#CLASS_TYPES) {
next if !$NEIGH{$class};
print "$_\n" for #{ $NEIGH{$class} };
}
or just
my %NEIGH = (
INTRA => [ "1.1.1.1/32", "2.2.2.2/32" ],
BB => [ "3.3.3.3/32", "4.4.4.4/32" ],
);
for my $class (keys(%NEIGH)) {
print "$_\n" for #{ $NEIGH{$class} };
}
My goal is to convert this
my #array=("red", "blue", "green", ["purple", "orange"]);
into this
my #array = ( ["red"], ["blue"], ["green"], ["purple", "orange"]);
Current test code:
my #array = ("red", "blue", "green", ["purple", "orange"] );
foreach $item ( #array ) {
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
if(ref($item) ne 'ARRAY'){
#It's an array reference...
#you can read it with $item->[1]
#or dereference it uisng #newarray = #{$item}
#print "We've got an array!!\n";
print $item, "\n";
# keep a copy of our string
$temp = $item;
# re-use the variable but convert to an empty list
#item = ();
# add the temp-copy as first list item
#item[0] = $temp;
# print each list item (should be just one item)
print "$_\n" for $item;
}else{
#not an array in any way...
print "ALREADY an array!!\n";
}
}
# EXPECTED my #array=(["red"], ["blue"], ["green"], ["purple", "orange"]);
print #array , "\n";
foreach $item (#array){
if(ref($item) ne 'ARRAY'){
#
#say for $item;
print "didn't convert properly to array\n";
}
}
The comment about python maps pretty directly to perl.
my #array = ("red", "blue", "green", ["purple", "orange"] );
foreach $item ( #array ) {
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
if (ref $item ne 'ARRAY') {
$item = [ $item ];
}
}
though using map as in Borodin's answer would be more natural.
I'm wondering why you want to do this, but it's
#array = map { ref ? $_ : [ $_ ] } #array
And please don't call arrays #array; that's what the # is for.
Your comment is ridiculous
#if this was Python, it would be as simple as:
# if is not instance(item, array):
# # item wasn't a list
# item = [item]
If you were familiar with Perl then you wouldn't need to ask the question. You must be aware that there is no one-to-one translation from Python to Perl. Python is much less expressive than either Perl or C, but I can't imagine you demanding a simple conversion to C.
Please get over your bigotry.
If you push the values to a new array, you don't need to do more than evaluate whether or not $item is an arrayref:
#! perl
use strict;
use warnings;
use Data::Dumper;
my #array=("red", "blue", "green", ["purple", "orange"]);
my #new_array;
foreach my $item (#array) {
if ( ref($item) eq 'ARRAY' ) {
push #new_array, $item;
}
else {
push #new_array, [$item];
}
}
print Dumper \#new_array;
Output from Dumper:
$VAR1 = [
[
'red'
],
[
'blue'
],
[
'green'
],
[
'purple',
'orange'
]
];
After a long day of learning more Perl than I ever thought/wanted to learn... here's what I think is a workable solution:
#! perl
use strict;
use warnings;
use Data::Dumper;
my %the_dict = (duts =>
{dut_a => {UDF => 'hamnet'},
dut_b => {UDF => [ '1', '2', '3', ]},
dut_c => {UDF => [ 'Z' ], }});
print Dumper \%the_dict;
foreach my $dut (keys %{$the_dict{duts}}) {
# convert the dut's UDF value to an array if it wasn't already
if ( 'ARRAY' ne ref $the_dict{duts}->{$dut}{UDF} ) {
$the_dict{duts}->{$dut}{UDF} = [ $the_dict{duts}->{$dut}{UDF} ];
}
# now append a new value to the array
push(#{$the_dict{duts}{$dut}{UDF}}, 'works');
}
print Dumper \%the_dict;
when run we see these print-outs:
$VAR1 = {
'duts' => {
'dut_a' => {
'UDF' => 'hamnet'
},
'dut_c' => {
'UDF' => [
'Z'
]
},
'dut_b' => {
'UDF' => [
'1',
'2',
'3'
]
}
}
};
$VAR1 = {
'duts' => {
'dut_a' => {
'UDF' => [
'hamnet',
'works'
]
},
'dut_c' => {
'UDF' => [
'Z',
'works'
]
},
'dut_b' => {
'UDF' => [
'1',
'2',
'3',
'works'
]
}
}
};
Here's an example of an array I'm working with:
arr = [ "id1", "name1", "id2", "name2", "id3", "name3" ]
I want to change it into a new array that looks like:
new_arr = [ "id1: name1", "id2: name2", "id3: name3" ]
My attempt:
ids = arr.select.with_index { |_, i| i.even? }
names = arr.select.with_index { |_, i| i.odd? }
new_arr = ids.map { |i| i + ": " + names[ids.index(i)] }
Is there a better or more expressive way of doing this (potentially in a one-liner)?
I would use each_slice and string interpolation.
arr.each_slice(2).map { |(a, b)| "#{a}: #{b}" }
#=> ["id1: name1", "id2: name2", "id3: name3"]
Richard Hamilton`s comment made me think about the performance of the different solutions:
require 'benchmark'
arr = [ "id1", "name1", "id2", "name2", "id3", "name3" ]
slices = arr.each_slice(2)
n = 1_000_000
Benchmark.bmbm(15) do |x|
x.report("hashified :") { n.times do; Hash[*arr].map { |e| e.join ': ' } ; end }
x.report("concatenation :") { n.times do; slices.map { |a| a[0] + ": " + a[1] } ; end }
x.report("array join :") { n.times do; slices.map { |a| a.join(': ') } ; end }
x.report("interpolated :") { n.times do; slices.map { |(a, b)| "#{a}: #{b}" } ; end }
end
# Rehearsal ---------------------------------------------------
# hashified : 3.520000 0.030000 3.550000 ( 3.561024)
# concatenation : 2.300000 0.010000 2.310000 ( 2.315952)
# array join : 3.020000 0.010000 3.030000 ( 3.032235)
# interpolated : 1.950000 0.000000 1.950000 ( 1.954937)
# ----------------------------------------- total: 10.840000sec
#
# user system total real
# hashified : 3.430000 0.040000 3.470000 ( 3.473274)
# concatenation : 2.320000 0.010000 2.330000 ( 2.332920)
# array join : 3.070000 0.010000 3.080000 ( 3.081937)
# interpolated : 1.950000 0.000000 1.950000 ( 1.956998)
You can use Enumerable's each_slice method to get an enumeration of 2-element arrays from your arr. You can then simply join these elements:
arr.each_slice(2).map{|a| a.join(': ')}
What happens here is the each_slice returns an Enumerator which yields the 2-element arrays. Since Enumerators also are Enumerable, you can just use map to change these 2-element arrays and join them to a String.
each_slice is silly :)
Hash[ "id1", "name1", "id2", "name2", "id3", "name3" ].
map { |e| e.join ': ' }
#⇒ [ "id1: name1", "id2: name2", "id3: name3" ]
Try using each_slice:
arr.each_slice(2).entries.map { |ar| ar.join(': ') }
#=> ["id1: name1", "id2: name2", "id3: name3"]
You should use each_slice for this
arr.each_slice(2).map { |a| a[0] + ": " + a[1] }
=> ["id1: name1", "id2: name2", "id3: name3"]
I am trying to find any intersections of elements within a hash of arrays in Perl
For example
my %test = (
Lot1 => [ "A","B","C"],
Lot2 => [ "A","B","C"],
Lot3 => ["C"],
Lot4 => ["E","F"],
);
The result I would be after is
Lot1 and Lot2 have AB
Lot1,Lot2 and Lot3 have C
Lot4 has E and F.
I think this could be done with a recursive function that effectively moves its way through the arrays and if an intersection between two arrays is found it calls itself recursively with the intersection found and the next array. The stopping condition would be running out of arrays.
Once the function is exited I would have to iterate through the hash to get the arrays that contain these values.
Does this sound like a good approach? I have been struggling with the code, but was going to use List::Compare to determine the intersection.
Thank you.
Array::Utils has an intersection operation where you can test the intersect of two arrays. But that's only the start point of what you're trying to do.
So I would be thinking that you need to first invert your lookup:
my %member_of;
foreach my $key ( keys %test ) {
foreach my $element ( #{$test{$key}} ) {
push ( #{$member_of{$element}}, $key );
}
}
print Dumper \%member_of;
Giving:
$VAR1 = {
'A' => [
'Lot1',
'Lot2'
],
'F' => [
'Lot4'
],
'B' => [
'Lot1',
'Lot2'
],
'E' => [
'Lot4'
],
'C' => [
'Lot1',
'Lot2',
'Lot3'
]
};
Then collapse that, into a key set:
my %new_set;
foreach my $element ( keys %member_of ) {
my $set = join( ",", #{ $member_of{$element} } );
push( #{ $new_set{$set} }, $element );
}
print Dumper \%new_set;
Giving:
$VAR1 = {
'Lot1,Lot2,Lot3' => [
'C'
],
'Lot1,Lot2' => [
'A',
'B'
],
'Lot4' => [
'E',
'F'
]
};
So overall:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %test = (
Lot1 => [ "A", "B", "C" ],
Lot2 => [ "A", "B", "C" ],
Lot3 => ["C"],
Lot4 => [ "E", "F" ],
);
my %member_of;
foreach my $key ( sort keys %test ) {
foreach my $element ( #{ $test{$key} } ) {
push( #{ $member_of{$element} }, $key );
}
}
my %new_set;
foreach my $element ( sort keys %member_of ) {
my $set = join( ",", #{ $member_of{$element} } );
push( #{ $new_set{$set} }, $element );
}
foreach my $set ( sort keys %new_set ) {
print "$set contains: ", join( ",", #{ $new_set{$set} } ), "\n";
}
I don't think there's a more efficient way to tackle it, because you're comparing each array to each other array, and forming a new compound key out of it.
This gives you:
Lot1,Lot2 contains: A,B
Lot1,Lot2,Lot3 contains: C
Lot4 contains: E,F
This can be done as two simple hash conversions:
Build a hash that lists all of the lots each item is in
Convert that to a hash that lists all items for each lot combination
Then just dump the last hash in a convenient form
This is the code.
use strict;
use warnings 'all';
use feature 'say';
my %test = (
Lot1 => [ "A", "B", "C" ],
Lot2 => [ "A", "B", "C" ],
Lot3 => ["C"],
Lot4 => [ "E", "F" ],
);
my %items;
for my $lot ( keys %test ) {
for my $item ( #{ $test{$lot} } ) {
push #{ $items{$item} }, $lot;
}
}
my %lots;
for my $item ( keys %items ) {
my $lots = join '!', sort #{ $items{$item} };
push #{ $lots{$lots} }, $item;
}
for my $lots ( sort keys %lots ) {
my #lots = split /!/, $lots;
my $items = join '', #{ $lots{$lots} };
$lots = join ', ', #lots;
$lots =~ s/.*\K,/ and/;
printf "%s %s %s\n", $lots, #lots > 1 ? 'have' : 'has', $items;
}
output
Lot1 and Lot2 have AB
Lot1, Lot2 and Lot3 have C
Lot4 has EF
It generates an %items hash that looks like this
{
A => ["Lot2", "Lot1"],
B => ["Lot2", "Lot1"],
C => ["Lot2", "Lot3", "Lot1"],
E => ["Lot4"],
F => ["Lot4"],
}
and from that a %lots hash that looks like this
{
"Lot1!Lot2" => ["A", "B"],
"Lot1!Lot2!Lot3" => ["C"],
"Lot4" => ["E", "F"],
}
I'm trying to compare 2 huge arrays and want to use map. I am using unique key concept here to match the indexes.
My Arrays:
my #array1 = ( ['a','b','c','d'], ['e','f','g','h'], ['i','j','k','l'], ['m','n','o','p'], ['q','r','s','t']);
my #array2 = ( ['r','q','s','t'], ['b','a','c','d'], ['n','m','o','p'], ['f','e','g','h'], ['j','i','k','l']);
My unique Keys:
my #uk1 = (0,2,3);
my #uk2 = (1,2,3);
These arrays will be huge in size, over 30,000 indexes in each with over 20 elements in each index.
So effectively i create a map where
for ( my $j = 0; $j <= $#array1 ; $j++ )
{
my searchString;
for ( my $k = 0; $k <= $#uk1; $k++ )
{
if ( $k != 0 )
{
$searchString .= ","
}
$my searchString .= $array1[$j][$uk[$k];
}
my #result = map { $_ }
grep { join (",",$array2[$_][1],$array2[$_][2],$array2[$_][3]) ) =~ join(",",$array1[$j][0],$array1[$j][1],$array1[$j][2]) }
0 .. $#array;
}
returns matched indexes.
My problem is, how do i make this dependant on the unique keys? as the length of the unique key will keep changing and as far as i know i cannot dynamically create the $array2[$_] join part.
Hope my question is clear enough.
I want to have the logic that compares
$array1[$uk1[0]],$array1[$uk1[1]],$array1[$uk1[2]] and so on (depending on the number of keys in UK) with
$array2[$uk2[0]],$array2[$uk2[1]],$array2[$uk2[2]].......
Perhaps,
my #array1 = ( ['a','b','c','d'], ['e','f','g','h'], ['i','j','k','l'], ['m','n','o','p'], ['q','r','s','t']);
my #array2 = ( ['r','q','s','t'], ['b','a','c','d'], ['n','m','o','p'], ['f','e','g','h'], ['j','i','k','l']);
my #result;
for my $i (0 .. $#array1) {
push #result,
map { [$i, $_] }
grep {
"#{ $array1[$i] }[1,2,3]" eq "#{ $array2[$_] }[0,2,3]"
}
0 .. $#array2;
}
use Data::Dumper; print Dumper \#result;
output
$VAR1 = [
[
0,
1
],
[
1,
3
],
[
2,
4
],
[
3,
2
],
[
4,
0
]
];
What you want to use is an array slice:
But lets also make life easier:
for my $sample ( #array1 )
{
my $test= join(",", #$sample[#uk1]) ;
my #result = grep { $_ eq $test } map { join(",", #$_[#uk2] ) } #array2 ;
say "huzzah" if #result ;
}
Perl lets you specify multiple elements from an array via the "array slice":
my #list= ('a', 'b', 'c', 'd') ;
my #pieces= #list[1,3] ;