How to save Second Dimension Of Hash In Order Perl - 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);

Related

How to print first element of subhash in reverse manner 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";
}

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.

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>

How can I sort a Hash of hashes of Array of Arrays by (inner) array in value in Perl [duplicate]

This question already has an answer here:
Sorting a Hash of Hashes of Array of Arrays in Perl
(1 answer)
Closed 7 years ago.
I have this data-structure, product of my data:
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4' => {
'-1.19 +' => [
[
'+', '2', '120', '0.76', '0.809799'
]
],
'-1.19 -' => [
[
'-', '2', '120', '0.19', '0.585090'
]
]
}
I want to organize it according to the last value of the 'inside' array that corresponds to the data grouped by a 'inside' key, in this case: '-1.19 +' and '-1.19 -'. Because I want to put the the key that has the greatest value of the inside array, on the bottom of my hash.
In other words:
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4' => {
'-1.19 -' => [
[
'-', '2', '120', '0.19', '0.585090'
]
],
'-1.19 +' => [
[
'+', '2', '120', '0.76', '0.809799'
]
]
}
How Can I do this task, because I want to select the key that has the greater value, so I want to shift the other ones.
Any suggestions? Thanks in advance.
To get the last element, use the index of -1.
my $inner_hash = $hash{'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4'};
my #sorted = sort { $inner_hash->{$a}[-1]
<=>
$inner_hash->{$b}[-1]
} keys %$inner_hash;
print "#sorted\n";
To sort a list by comparing some expression on each element individually, you might like to use the collection of functions in List::UtilsBy called sort_by. Specifically in this case as the key expression is returning a number to be compared numerically, you'll want to use nsort_by:
use List::UtilsBy 'nsort_by';
my #sorted = nsort_by { $_->[-1] } #items;
Keys in the hash doesn't have any order so if you want to have keys in order you have to change your inner hash into an array.
my %hash = (
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4'
=> {
'-1.19 +' => [ [ '+', '2', '120', '0.76', '0.809799' ] ],
'-1.19 -' => [ [ '-', '2', '120', '0.19', '0.585090' ] ]
}
);
for my $value ( values %hash ) {
$value = [
map +[ $_, $value->{$_} ],
sort { $value->{$a}[0][-1] <=> $value->{$b}[0][-1] } keys %$value
];
}
The result will be
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4'
=> [
[ '-1.19 -', [ [ '-', '2', '120', '0.19', '0.585090' ] ] ],
[ '-1.19 +', [ [ '+', '2', '120', '0.76', '0.809799' ] ] ]
]
I would recommend removing double array in inside so code would look like:
for my $value ( values %hash ) {
$value = [
map +[ $_, $value->{$_}[0] ],
sort { $value->{$a}[0][-1] <=> $value->{$b}[0][-1] } keys %$value
];
}
and the result:
'locus4 >9.+.2668516.2768570.GABA3.1.54.6586237.2718516.2718570 45880 46000 locus4'
=> [
[ '-1.19 -', [ '-', '2', '120', '0.19', '0.585090' ] ],
[ '-1.19 +', [ '+', '2', '120', '0.76', '0.809799' ] ]
]

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);