How to print first element of subhash in reverse manner Perl - perl

I want to print the values of every first $family reversely, and the first value it prints is the total scalar value in each $community.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Tie::Autotie 'Tie::IxHash';
my #NAMES = qw(AA AB AC BA BB BC CA CB CC AA AB AC BA BB BC CA CB CC AA AB AC BA BB BC CA CB CC AD CD CE CF BD BE);
my #FAMILIES = qw(A A A B B B C C C A A A B B B C C C A A A B B B C C C A C C C B B);
my #COMMUNITIES = qw(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 0 2 2 2 1 1);
tie my %community_family_name, 'Tie::IxHash' ;
undef $community_family_name{ $COMMUNITIES[$_] }{ $FAMILIES[$_]}{$NAMES[$_]}
for 0 .. $#NAMES;
for my $community (keys %community_family_name) {
my $i = 0;
for my $family (keys %{ $community_family_name{$community} }) {
for my $name (keys %{ $community_family_name{$community}{$family} }) {
$community_family_name{$community}{$family}{$name} = $i++;
}
}
}
foreach my $community (sort keys %community_family_name)
{
foreach my $family (keys %{ $community_family_name{$community} })
{
foreach my $name (keys %{ $community_family_name{$community}{$family}})
{
print "[$community] = ";
print scalar "%{ $community_family_name{$community}";
print reverse "$community_family_name{$community}{$family}{$name}";
print " ";
}
}
}
print Dumper(\%community_family_name);
Current output:
[0] = HASH(0x1038c80)0 [0] = HASH(0x1038c80)1 [0] = HASH(0x1038c80)2 [0] = HASH(0x1038c80)3 [0] = HASH(0x1038c80)4 [0] = HASH(0x1038c80)5 [0] = HASH(0x1038c80)6 ...
Expected output:
[0] = 10 7 4 0
[1] = 11 8 3 0
[2] = 12 6 3 0
What is in the %community_family_name
$VAR1 = {
'0' => {
'A' => {
'AA' => [
0
],
'AB' => [
1
],
'AC' => [
2
],
'AD' => [
3
]
},
'B' => {
'BA' => [
4
],
'BB' => [
5
],
'BC' => [
6
]
},
'C' => {
'CA' => [
7
],
'CB' => [
8
],
'CC' => [
9
]
}
},
'1' => {
'A' => {
'AA' => [
0
],
'AB' => [
1
],
'AC' => [
2
]
},
'B' => {
'BA' => [
3
],
'BB' => [
4
],
'BC' => [
5
],
'BD' => [
6
],
'BE' => [
7
]
},
'C' => {
'CA' => [
8
],
'CB' => [
9
],
'CC' => [
10
]
}
},
'2' => {
'A' => {
'AA' => [
0
],
'AB' => [
1
],
'AC' => [
2
]
},
'B' => {
'BA' => [
3
],
'BB' => [
4
],
'BC' => [
5
]
},
'C' => {
'CA' => [
6
],
'CB' => [
7
],
'CC' => [
8
],
'CD' => [
9
],
'CE' => [
10
],
'CF' => [
11
]
}
}
};

You are almost there.
The first point is your expected result is per community, so your print functions must stay in the first loop.
The second is you must use scalar and reverse to arrays, not to scalar value such as double-quoted strings.
# Let me shorten your variable name, that's way too long...
my %cfn = %community_family_name;
foreach my $com (sort keys %cfn) {
# Count up keys in the community
my $count = map { keys %{$cfn{$com}->{$_}} } keys %{$cfn{$com}};
my #vals;
foreach my $family (reverse sort keys %{$cfn{$com}}) {
my ($first) = sort keys %{$cfn{$com}->{$family}};
push #vals, #{$cfn{$com}->{$family}->{$first}};
}
printf "[%d] = %s", $com, join q{ }, $count, #vals;
print "\n";
}

Related

How to increment the value of hash for every subshash Perl

I want to increment the value of hash starting from 0 for each $COMMUNITY, I define an array #indicator from 0 until the same index of the array #NAME and push it to become the value, but it is not I want exactly, and I don't know how to this. I know very well that the value is not in the order because the place in array in not organized first, but how to do this exactly, and then,
how to print every first value of subhash $FAMILY.
for example,
community 0 = name 0 4 7
community 1 = name 0 3 8
community 2 = name 0 3 6
#!/usr/bin/perl
use warnings;
use strict;
use Tie::Autotie 'Tie::IxHash';
my #NAME= qw(AA AB AC BA BB BC CA CB CC AA AB AC BA BB BC CA CB CC AA AB AC BA BB BC CA CB CC AD CD CE CF BD BE);
my #FAMILY= qw(A A A B B B C C C A A A B B B C C C A A A B B B C C C A C C C B B);
my #COMMUNITY= qw(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 0 2 2 2 1 1);
tie my %COMMUNITY_FAMILY_NAME,'Tie::IxHash' ;
my #indicator;
foreach (my $x=0;$x<=scalar #NAME;$x++)
{
push #indicator,$x;
}
push #{ $COMMUNITY_FAMILY_NAME{ $COMMUNITY[$_] }{ $FAMILY[$_]}{$NAME[$_]} }, $indicator[$_] for 0 .. $#NAME;
print Dumper(\%COMMUNITY_FAMILY_NAME);
Output:
$VAR1 = {
'0' => {
'A' => {
'AA' => [
0
],
'AB' => [
1
],
'AC' => [
2
],
'AD' => [
27
]
},
'B' => {
'BA' => [
3
],
'BB' => [
4
],
'BC' => [
5
]
},
'C' => {
'CA' => [
6
],
'CB' => [
7
],
'CC' => [
8
]
}
},
'1' => {
'A' => {
'AA' => [
9
],
'AB' => [
10
],
'AC' => [
11
]
},
'B' => {
'BA' => [
12
],
'BB' => [
13
],
'BC' => [
14
],
'BD' => [
31
],
'BE' => [
32
]
},
'C' => {
'CA' => [
15
],
'CB' => [
16
],
'CC' => [
17
]
}
},
'2' => {
'A' => {
'AA' => [
18
],
'AB' => [
19
],
'AC' => [
20
]
},
'B' => {
'BA' => [
21
],
'BB' => [
22
],
'BC' => [
23
]
},
'C' => {
'CA' => [
24
],
'CB' => [
25
],
'CC' => [
26
],
'CD' => [
28
],
'CE' => [
29
],
'CF' => [
30
]
}
}
};
Expected output:
$VAR1 = {
'0' => {
'A' => {
'AA' => [
0
],
'AB' => [
1
],
'AC' => [
2
],
'AD' => [
3
]
},
'B' => {
'BA' => [
4
],
'BB' => [
5
],
'BC' => [
6
]
},
'C' => {
'CA' => [
7
],
'CB' => [
8
],
'CC' => [
9
]
}
},
'1' => {
'A' => {
'AA' => [
0
],
'AB' => [
1
],
'AC' => [
2
]
},
'B' => {
'BA' => [
3
],
'BB' => [
4
],
'BC' => [
5
],
'BD' => [
6
],
'BE' => [
7
]
},
'C' => {
'CA' => [
8
],
'CB' => [
9
],
'CC' => [
10
]
}
},
'2' => {
'A' => {
'AA' => [
0
],
'AB' => [
1
],
'AC' => [
2
]
},
'B' => {
'BA' => [
3
],
'BB' => [
4
],
'BC' => [
5
]
},
'C' => {
'CA' => [
6
],
'CB' => [
7
],
'CC' => [
8
],
'CD' => [
9
],
'CE' => [
10
],
'CF' => [
11
]
}
}
};
You need to iterate the elements by communities, resetting the counter for each. So, I first created the structure with no counters, and then iterated over it in the correct order while setting the values.
#!/usr/bin/perl
use strict;
use warnings;
use Tie::Autotie 'Tie::IxHash';
my #NAMES = qw(AA AB AC BA BB BC CA CB CC AA AB AC BA BB BC CA CB CC AA AB AC BA BB BC CA CB CC AD CD CE CF BD BE);
my #FAMILIES = qw(A A A B B B C C C A A A B B B C C C A A A B B B C C C A C C C B B);
my #COMMUNITIES = qw(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 0 2 2 2 1 1);
tie my %community_family_name, 'Tie::IxHash' ;
undef $community_family_name{ $COMMUNITIES[$_] }{ $FAMILIES[$_]}{$NAMES[$_]}
for 0 .. $#NAMES;
for my $community (keys %community_family_name) {
my $i = 0;
for my $family (keys %{ $community_family_name{$community} }) {
for my $name (keys %{ $community_family_name{$community}{$family} }) {
$community_family_name{$community}{$family}{$name} = [$i++];
}
}
}
use Data::Dumper; print Dumper(\%community_family_name);
Note: Are you sure the values need to be in an array ref? There's never more than one value.
By convention, lowercase names are used for mutable variables in Perl.

How to save Second Dimension Of Hash In Order Perl

How I can save these Arrays into this Hash Of Arrays in order, not following alphabetically order, just save according to the Arrays.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- stackoverflow won't allow me to post because not enough details, so I did this -
#!/usr/bin/perl
use warnings;
use strict;
use Tie::IxHash;
use Data::Dumper;
my #NAME= qw(AA AB AC BA BB BC CA CB CC AA AB AC BA BB BC CA CB CC AA AB AC BA BB BC CA CB CC AD CD CE CF BD BE);
my #FAMILY= qw(A A A B B B C C C A A A B B B C C C A A A B B B C C C A C C C B B);
my #COMMUNITY= qw(0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 0 2 2 2 1 1);
tie my %COMMUNITY_FAMILY_NAME,'Tie::IxHash' ;
push #{ $COMMUNITY_FAMILY_NAME{ $COMMUNITY[$_] }{ $FAMILY[$_] } }, $NAME[$_] for 0 .. $#NAME;
print Dumper(\%COMMUNITY_FAMILY_NAME);
Output:
$VAR1 = {
'0' => {
'A' => [
'AA',
'AB',
'AC',
'AD'
],
'C' => [
'CA',
'CB',
'CC'
],
'B' => [
'BA',
'BB',
'BC'
]
},
'1' => {
'A' => [
'AA',
'AB',
'AC'
],
'C' => [
'CA',
'CB',
'CC'
],
'B' => [
'BA',
'BB',
'BC',
'BD',
'BE'
]
},
'2' => {
'A' => [
'AA',
'AB',
'AC'
],
'C' => [
'CA',
'CB',
'CC',
'CD',
'CE',
'CF'
],
'B' => [
'BA',
'BB',
'BC'
]
}
};
Expected Output:
$VAR1 = {
'0' => {
'A' => [
'AA',
'AB',
'AC',
'AD'
],
'B' => [
'BA',
'BB',
'BC'
]
'C' => [
'CA',
'CB',
'CC'
],
},
'1' => {
'A' => [
'AA',
'AB',
'AC'
],
'B' => [
'BA',
'BB',
'BC',
'BD',
'BE'
'C' => [
'CA',
'CB',
'CC'
],
]
},
'2' => {
'A' => [
'AA',
'AB',
'AC'
],
'B' => [
'BA',
'BB',
'BC'
]
'C' => [
'CA',
'CB',
'CC',
'CD',
'CE',
'CF'
],
}
};
Your subhashes are not tied to Tie::IxHash, and thus they are plain/unordered. You can change that by tie every subhash before usage.
# push #{ $COMMUNITY_FAMILY_NAME{ $COMMUNITY[$_] }{ $FAMILY[$_] } }, $NAME[$_] for 0 .. $#NAME;
for (0 .. $#NAME) {
my $href = $COMMUNITY_FAMILY_NAME{ $COMMUNITY[$_] } ||= do {
tie my %hash, 'Tie::IxHash';
\%hash;
};
push #{ $href->{ $FAMILY[$_] } }, $NAME[$_];
}
The issue you're running into is that a tied hash only affects one level - each value of a tied hash is just whatever you store in it, which usually is a normal perl value. That means if you want a multi-dimensional tied hash, both the top level variable has to be tied, and any values stored in it (In this case, hashrefs) as well.
For example:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
use Tie::IxHash;
tie my %hash, 'Tie::IxHash';
tie %{$hash{"b"}}, 'Tie::IxHash';
tie %{$hash{"c"}}, 'Tie::IxHash';
$hash{"b"}->{"b"} = [ qw/1 2 3/ ];
$hash{"b"}->{"a"} = [ qw/4 5 6/ ];
$hash{"c"}->{"d"} = [ qw/7 8 9/ ];
$hash{"c"}->{"c"} = [ qw/10 11 12/ ];
print Dumper(\%hash);

Issue accessing Hash in perl

I have a Hash of following structure in perl -
my %testHash = (
KeyL1 => {
KeyLL1 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
KeyLL2 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
KeyLL3 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
},
KeyL2 => {
KeyLL1 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
KeyLL2 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
KeyLL3 => {
KeyLLL1 => [1,2],
KeyLLL2 => [2,3],
},
},
);
Now, when I am trying to access it the following way, I am getting 'undef' as a result
my %tempHash = $testHash{'KeyL1'};
print Data::Dumper::Dumper($tempHash{'KeyLL1'});
print Data::Dumper::Dumper($tempHash{'KeyLL1'}{'KeyLLL1'});
Result --
$VAR1 = undef; $VAR1 = undef;
Please point to me what am I doing wrong. I am pretty new to perl.
The value of $testHash{'KeyL1'} is a hashref, not a hash.
Hashrefs are scalars. my %tempHash = is not expecting a scalar.
You need to dereference it:
my %tempHash = %{$testHash{'KeyL1'}};
Also, you could do it this way if its just about viewing the structures.
Also try:
print Dumper $testHash{KeyL1} ;
print Dumper $testHash{KeyL1}{KeyLL1} ;
print Dumper $testHash{KeyL1}{KeyLL1}{KeyLLL1} ;
Output:
%_Host#User> ./hash.pl
$VAR1 = {
'KeyLL1' => {
'KeyLLL2' => [
2,
3
],
'KeyLLL1' => [
1,
2
]
},
'KeyLL2' => {
'KeyLLL2' => [
2,
3
],
'KeyLLL1' => [
1,
2
]
},
'KeyLL3' => {
'KeyLLL2' => [
2,
3
],
'KeyLLL1' => [
1,
2
]
}
};
$VAR1 = {
'KeyLLL2' => [
2,
3
],
'KeyLLL1' => [
1,
2
]
};
$VAR1 = [
1,
2
];
%_Host#User>

Merge hashes with arrays with Hash::Merge

I am trying to merge two hashes which contains one or more arrays using Hash::Merge. For example:
use strict;
use warnings;
use feature qw(say);
use Data::Dump qw(dump);
use Hash::Merge qw(merge);
my $h1 = { a => [ { aa => 1 }, 3 ] };
my $h2 = { a => [ { bb => 2 } ] };
my $hLeft = merge( $h1, $h2 );
my $hRight = merge( $h2, $h1 );
say " hLeft: " . dump($hLeft);
say " hRight: " . dump($hRight);
my $hDesired = { a => [ { aa => 1, bb => 2 }, 3 ] };
say "Desired: " . dump($hDesired);
This gives output:
hLeft: { a => [{ aa => 1 }, 3, { bb => 2 }] }
hRight: { a => [{ bb => 2 }, { aa => 1 }, 3] }
Desired: { a => [{ aa => 1, bb => 2 }, 3] }
How can I get the correct output using Hash::Merge ?
This can be done using Hash::Merge::specify_behavior :
use warnings;
use strict;
use Data::Dump 'dump';
use Hash::Merge;
use feature 'say';
Hash::Merge::specify_behavior
( {
'SCALAR' => {
'SCALAR' => sub { $_[1] },
'ARRAY' => sub { [ $_[0], #{$_[1]} ] },
'HASH' => sub { $_[1] },
},
'ARRAY' => {
'SCALAR' => sub { $_[1] },
'ARRAY' => \&mergeArrays,
'HASH' => sub { $_[1] },
},
'HASH' => {
'SCALAR' => sub { $_[1] },
'ARRAY' => sub { [ values %{$_[0]}, #{$_[1]} ] },
'HASH' => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) },
},
},
'My Behavior',
);
my $h1={a=>[{aa=>1},3]};
my $h2={a=>[{bb=>2}]};
my $hMerge=Hash::Merge::merge($h1,$h2);
say "hMerge: ".dump($hMerge);
sub mergeArrays{
my ($a,$b)=#_;
my ($na,$nb)=($#$a,$#$b);
my #c;
if ($na>$nb) {
#c=#$a[($nb+1)..$na];
return mergeArrays2($a,$b,\#c,$nb);
} else {
#c=#$b[($na+1)..$nb];
return mergeArrays2($a,$b,\#c,$na);
}
}
sub mergeArrays2{
my ($a,$b,$c,$n)=#_;
my $r=[];
for my $i (0..$n) {
if (ref($a->[$i]) && ref($b->[$i])) {
push(#$r,Hash::Merge::_merge_hashes($a->[$i],$b->[$i]));
} else {
push(#$r,$a->[$i]);
}
}
push(#$r,#$c);
return $r;
}
Output:
hMerge: { a => [{ aa => 1, bb => 2 }, 3] }
The default behavior for merging arrays is to append them:
sub { [ #{$_[0]}, #{$_[1]} ] },
To get different behavior, one must use Hash::Merge::specify_behavior.
The following solution is LEFT_PRECEDENT, and merges arrays element to element:
use strict;
use warnings;
use feature qw(say);
use Data::Dump qw(dump);
use Hash::Merge qw(merge);
Hash::Merge::specify_behavior(
{ 'SCALAR' => {
'SCALAR' => sub { $_[0] },
'ARRAY' => sub { $_[0] },
'HASH' => sub { $_[0] },
},
'ARRAY' => {
'SCALAR' => sub { [ #{ $_[0] }, $_[1] ] },
'ARRAY' => sub {
my ( $left, $right ) = #_;
my #merged = #$left;
my #to_add = #$right;
for (#merged) {
last if !#to_add;
$_ = Hash::Merge::merge( $_, shift #to_add );
}
return [ #merged, #to_add ];
},
'HASH' => sub { [ #{ $_[0] }, values %{ $_[1] } ] },
},
'HASH' => {
'SCALAR' => sub { $_[0] },
'ARRAY' => sub { $_[0] },
'HASH' => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) },
},
},
'My Behavior',
);
my $h1 = { a => [ { aa => 1 }, 3 ] };
my $h2 = { a => [ { bb => 2 } ] };
my $merged = merge( $h1, $h2 );
say "Merged: " . dump($merged);
Outputs:
Merged: { a => [{ aa => 1, bb => 2 }, 3] }

Hash merge/concatenation

this is a dump of my hashes: %hash1
$VAR1 = {
abc => {
123 => [
'xx',
'yy',
'zy'
],
456 => [
'ab',
'cd',
'ef'
]
}
};
and the second one: %hash2
$VAR2 = {
def => {
659 => [
'wx',
'yg',
'kl'
],
456 => [
'as',
'sd',
'df'
]
},
abc => {
987 => [
'lk',
'dm',
'sd'
]
}
};
Now I want to merge these two hashes in a new hash, but if a key is duplicated (here 'abc'), the values should be appended, not replaced, so the keys should remain unique, and all the values should be retained as well. How can this be done in Perl?
The output should be as follows:
$VAR1 = {
def => {
659 => [
'wx',
'yg',
'kl'
],
456 => [
'as',
'sd',
'df'
]
},
abc => {
987 => [
'lk',
'dm',
'sd'
],
123 => [
'xx',
'yy',
'zy'
],
456 => [
'ab',
'cd',
'ef'
]
}
};
Use the CPAN modules Hash::Merge or Hash::Merge::Simple. The first is highly configurable and the second is very simple to use.
for my $x (keys(%h2)) {
for my $y (keys(%{ $h2{$x} })) {
push #{ $h1{$x}{$y} }, #{ $h2{$x}{$y} };
}
}
For the sample data provided, the following would perform the merging you describe:
my %merged = map {
$_ => {
%{$a{$_} // {}},
%{$b{$_} // {}}
}
} ( keys %a, keys %b );
Test:
use strict;
use warnings;
use Data::Dump 'dd';
my %a = (
abc => {
123 => [
'xx',
'yy',
'zy'
],
456 => [
'ab',
'cd',
'ef'
]
}
);
my %b = (
def => {
659 => [
'wx',
'yg',
'kl'
],
456 => [
'as',
'sd',
'df'
]
},
abc => {
987 => [
'lk',
'dm',
'sd'
]
}
);
my %merged = map {
$_ => {
%{$a{$_} // {}},
%{$b{$_} // {}}
}
} ( keys %a, keys %b );
dd \%merged;
# {
# abc => {
# 123 => ["xx", "yy", "zy"],
# 456 => ["ab", "cd", "ef"],
# 987 => ["lk", "dm", "sd"],
# },
# def => { 456 => ["as", "sd", "df"], 659 => ["wx", "yg", "kl"] },
# }
sub merge_hashes {
my ($h1, $h2) = #_;
foreach my $key (keys %$h2) {
if (!exists $h1->{$key} || ref($h1->{$key}) ne 'HASH' || ref($h2->{$key}) ne 'HASH') {
$h1->{$key} = $h2->{$key};
}
else {
merge_hashes($h1->{$key}, $h2->{$key});
}
}
}
merge_hashes(\%hash1, \%hash2);