order hash perl using another array - perl

Im trying to sort a Hash using a list/array
my $hash = { cta => '01340031810312074443',
ttr => '001',fil => '0000',
ref => '0000',
mef => '0000000000000060000',
mch => '0000000000000000000',
nli => '00000000',
tdi => 'V',
ndi => '006126952',
tdip => 'V',
ndip => '006126952',
};
#order = qw(cta ttr fil ref mef mch nli tdi ndi tdip ndip);
We know Perl dont save orders in hash but I need to print in that order. How can I do that?
Thanks

If you just want to print the values and not the keys, you can also use a hash slice:
use feature 'say';
say join "\t", #hash{#order};

for my $key (#order) {
print $key . ": " . $hash->{$key} . "\n";
}

Try this:
for (#order) {
print $_, " => ", $hash->{$_}, "\n";
}

Related

How to convert query string in hash in perl

I have a query string like this:
id=60087888;jid=16471827;from=advance;action=apply
or it can be like this :
id=60087888&jid=16471827&from=advance&action=apply
Now from this i want to create a hash that will have key as id and its value
I have done this
my %in;
$buffer = 'resid=60087888;jobid=16471827;from=advance;action=apply';
#pairs = split(/=/, $buffer);
foreach $pair (#pairs){
($name, $value) = split(/=/, $pair);
$in{$name} = $value;
}
print %in;
But the issue is in the query string it can be semin colon or & so how can we do this please help me
Don't try to solve it with new code; this is what CPAN modules are for. Specifically in this case, URI::Query
use URI::Query;
use Data::Dumper;
my $q = URI::Query->new( "resid=60087888;jobid=16471827;from=advance;action=apply" );
my %hash = $q->hash;
print Dumper( \%hash );
Gives
{ action => 'apply',
from => 'advance',
jobid => '16471827',
resid => '60087888' }
You've already an answer that works - but personally I might tackle it like this:
my %in = $buffer =~ m/(\w+)=(\w+)/g;
What this does is use regular expressions to pattern match either side of the equals sign.
It does so in pairs - effectively - and as a result is treated by a sequence of key-values in the hash assignment.
Note - it does assume you've not got special characters in your keys/values, and that you have no null values. (Or if you do, they'll be ignored - you can use (\w*) instead if that's the case).
But you get:
$VAR1 = {
'from' => 'advance',
'jid' => '16471827',
'action' => 'apply',
'id' => '60087888'
};
Alternatively:
my %in = map { split /=/ } split ( /[^=\w]/, $buffer );
We split using 'anything that isn't word or equals' to get a sequence, and then split on equals to make the same key-value pairs. Again - certain assumptions are made about valid delimiter/non-delimiter characters.
Check this answer:
my %in;
$buffer = 'resid=60087888;jobid=16471827;from=advance;action=apply';
#pairs = split(/[&,;]/, $buffer);
foreach $pair (#pairs){
($name, $value) = split(/=/, $pair);
$in{$name} = $value;
}
delete $in{resid};
print keys %in;
I know I'm late to the game, but....
#!/usr/bin/perl
use strict;
use CGI;
use Data::Dumper;
my $query = 'id=60087888&jid=16471827&from=advance&action=apply&blank=&not_blank=1';
my $cgi = CGI->new($query);
my %hash = $cgi->Vars();
print Dumper \%hash;
will produce:
$VAR1 = {
'not_blank' => '1',
'jid' => '16471827',
'from' => 'advance',
'blank' => '',
'action' => 'apply',
'id' => '60087888'
};
Which has the added benefit of dealing with keys that might not have values in the source string.
Some of the other examples will produce:
$VAR1 = {
'id' => '60087888',
'1' => undef,
'jid' => '16471827',
'from' => 'advance',
'blank' => 'not_blank',
'action' => 'apply'
};
which may not be desirable.
I would have used URI::Query #LeoNerd 's answer, but I didn't have the ability to install a module in my case and CGI.pm was handy.
also, you could
my $buffer = 'id=60087888&jid=16471827&from=advance&action=apply';
my %hash = split(/&|=/, $buffer);
which gives:
$hash = {
'jid' => '16471827',
'from' => 'advance',
'action' => 'apply',
'id' => '60087888'
};
This is VERY fragile, so I wouldn't advocate using it.

get nbest key-value pairs hash table in Perl

I have this script that use a hash table:
#!/usr/bin/env perl
use strict; use warnings;
my $hash = {
'cat' => {
"félin" => '0.500000',
'chat' => '0.600000',
'chatterie' => '0.300000'
'chien' => '0.01000'
},
'rabbit' => {
'lapin' => '0.600000'
},
'canteen' => {
"ménagère" => '0.400000',
'cantine' => '0.600000'
}
};
my $text = "I love my cat and my rabbit canteen !\n";
foreach my $word (split "\s+", $text) {
print $word;
exists $hash->{$word}
and print "[" . join(";", keys %{ $hash->{$word} }) . "]";
print " ";
}
For now, I have this output:
I love my cat[chat;félin;chatterie;chien] and my rabbit[lapin] canteen[cantine;ménagère] !
I need to have the nbest key value according to the frequencies (stored in my hash). For example, I want to have the 3 best translations according to the frequencies like this:
I love my cat[chat;félin;chatterie] and my rabbit[lapin] canteen[cantine;ménagère] !
How can I change my code to take into account the frequencies of each values and also to print the nbest values ?
Thanks for your help.
The tidiest way to do this is to write a subroutine that returns the N most frequent translations for a given word. I have written best_n in the program below to do that. It uses rev_nsort_by from List::UtilsBy to do the sort succinctly. It isn't a core module, and so may well need to be installed.
I have also used an executable substitution to modify the string in-place.
use utf8;
use strict;
use warnings;
use List::UtilsBy qw/ rev_nsort_by /;
my $hash = {
'cat' => {
'félin' => '0.500000',
'chat' => '0.600000',
'chatterie' => '0.300000',
'chien' => '0.01000',
},
'rabbit' => {
'lapin' => '0.600000',
},
'canteen' => {
'ménagère' => '0.400000',
'cantine' => '0.600000',
}
};
my $text = "I love my cat and my rabbit canteen !\n";
$text =~ s{(\S+)}{
$hash->{$1} ? sprintf '[%s]', join(';', best_n($1, 3)) : $1;
}ge;
print $text;
sub best_n {
my ($word, $n) = #_;
my $item = $hash->{$word};
my #xlate = rev_nsort_by { $item->{$_} } keys %$item;
$n = $n > #xlate ? $#xlate : $n - 1;
#xlate[0..$n];
}
output
I love my [chat;félin;chatterie] and my [lapin] [cantine;ménagère] !

How to incorporate hash inside hash in perl?

my %book = (
'name' => 'abc',
'author' => 'monk',
'isbn' => '123-890',
'issn' => '#issn',
);
my %chapter = (
'title' => 'xyz',
'page' => '90',
);
How do I incorporate %book inside %chapter through reference so that when I write "$chapter{name}", it should print 'abc'?
You can copy the keys/values of the %book into the %chapter:
#chapter{keys %book} = values %book;
Or something like
%chapter = (%chapter, %book);
Now you can say $chapter{name}, but changes in %book are not reflected in %chapter.
You can include the %book via reference:
$chapter{book} = \%book;
Now you could say $chapter{book}{name}, and changes do get reflected.
To have an interface that allows you to say $chapter{name} and that does reflect changes, some advanced techniques would have to be used (this is fairly trivial with tie magic), but don't go there unless you really have to.
You could write a subroutine to check a list of hashes for a key. This program demonstrates:
use strict;
use warnings;
my %book = (
name => 'abc',
author => 'monk',
isbn => '123-890',
issn => '#issn',
);
my %chapter = (
title => 'xyz',
page => '90',
);
for my $key (qw/ name title bogus / ) {
print '>> ', access_hash($key, \%book, \%chapter), "\n";
}
sub access_hash {
my $key = shift;
for my $hash (#_) {
return $hash->{$key} if exists $hash->{$key};
}
undef;
}
output
Use of uninitialized value in print at E:\Perl\source\ht.pl line 17.
>> abc
>> xyz
>>

Merging hashes in perl

I am trying to merge two hashes. Well, I am able to merge, but the output is not the way I want it to be:
Here is my code:
my %friend_list = (
Raj => "Good friend",
Rohit => "new Friend",
Sumit => "Best Friend",
Rohini => "Fiend",
Allahabad => "UttarPradesh",
);
my %city = (
Bangalore => "Karnataka",
Indore => "MadhyaPradesh",
Pune => "Maharashtra",
Allahabad => "UP",
);
my %friends_place = ();
my ($k, $v);
foreach my $ref (\%friend_list, \%city) {
while (($k,$v) = each (%$ref)) {
if (exists $ref{$k}) {
print"Warning: Key is all ready there\n";
next;
}
$friends_place{$k} = $v;
}
}
while (($k,$v) = each (%friends_place)) {
print "$k = $v \n";
}
From this o/p is
Raj=Good friend
Indore=MadhyaPradesh
Rohit=new Fiend
Bangalore=Karnataka
Allahabad=UttarPradesh
Sumit=Best Friend
Pune=Maharashtra
Rohini =Fiend
But I want to print %friend_list first followed by %city.
Another thing which I was trying to do is, if there is any duplicate key, then it should give me a warning message. But it is not giving me any message. As we can see here, we have Allahabad in both hash.
Thanks
Try with:
my %firend_list = (
Raj => "Good friend",
Rohit => "new Fiend",
Sumit => "Best Friend",
Rohini => "Fiend",
Allahabad => "UttarPradesh",
);
my %city = (
Bangalore => "Karnataka",
Indore => "MadhyaPradesh",
Pune => "Maharashtra",
Allahabad => "UP",
);
#merging
my %friends_place = ( %friend_list, %city );
And, for warnings:
foreach my $friend( keys %friend_list ){
print"Warning: Key is all ready there\n" if $friend ~~ [ keys %city ];
}
The line if (exists $ref{$k}) { is wrong and you can see it if you're putting use strict; use warnings; at the begining of the script.
Moreover this line should be if (exists $friends_place{$k}) { to produce the message about duplicate keys.
As hashes are unordered, you need to use an array to store the ordering:
my %friends_place = (%firend_list, %city);
my #friends_place_keyorder = ((keys %firend_list), (keys %city));
if ((scalar keys %friends_place) != (scalar #friends_place_keyorder)) {
print 'duplicate key found';
}
foreach (#friends_place_keyorder) {
print "$_ = $friends_place{$_}\n";
}
EDIT: my original solution in python, left here for historical purpose:
As hashes are unordered, you need to use an array to store the ordering. I don't know perl, so the following code is python (should be fairly straightforward to translate to perl):
friend_list = ...
city = ...
friends_place = dict(friend_list.items() + city.items())
friends_place_keyorder = friend_list.keys() + city.keys()
# detect duplicate keys by checking their lengths
# if there is duplicate then the hash would be smaller than the list
if len(friends_place) != len(friends_place_keyorder):
print "duplicate key found"
# iterate through the list of keys instead of the hashes directly
for k in friends_place_keyorder:
print k, friends_place[k]

Perl - Class::Struct Deferencing array

use Class::Struct;
struct (TimingStruct => {
_timingSense => '$',
_timingType => '$',
_relatedPin => '$',
_whenCond => '$'
});
struct (OutPinStruct => {
_outPinName => '$',
_outFunction => '$',
_timingarray => '#', #_timingarc => 'TimingStruct'
});
my #tarray = ();
my $t;
$t = TimingStruct->new(_timingSense => 'Unate',
_timingType => 'Wave',
_relatedPin => 'CO',
_whenCond => 'A ^ B'
);
push(#tarray, $t);
$t = TimingStruct->new(_timingSense => 'Combinational',
_timingType => 'Rising',
_relatedPin => 'ICO',
_whenCond => 'A ^ B ^ CI'
);
push(#tarray, $t);
my $op = OutPinStruct->new(_outPinName => "CO",
_outFunction => "A ^ B ^ CI",
_timingarray => \#tarray);
print $op->_outPinName . "\n";
print $op->_outFunction . "\n";
print $op->_timingarray . "\n";
my $t = ${${$op->_timingarray}[0]}[0];
print "\$t = \$op->_timingarray = $t->_timingSense() \n";
my #t = {$op->_timingarray};
print "\#t = \#{\$op->_timingarray} = $$t[1] \n";
Every output pin can have many timing-arcs and the OutPinStruct has a array to hold the timing-arcs. I'm not sure about de-referencing arrays(_timingarray) could someone tell me what is it that I'm doing wrongly?
Thanks.
$op->_timingarray is a "list of hashes". In general the keys to a hash are unordered and you cannot lookup the hash values through a numbered index. The elements you can access are
$op->_timingarray->[0]{'TimingStruct::_whenCond'}
$op->_timingarray->[0]{'TimingStruct::_timingSense'}
$op->_timingarray->[0]{'TimingStruct::_relatedPin'}
$op->_timingarray->[0]{'TimingStruct::_timingType'}
$op->_timingarray->[1]{'TimingStruct::_whenCond'}
$op->_timingarray->[1]{'TimingStruct::_timingSense'}
$op->_timingarray->[1]{'TimingStruct::_relatedPin'}
$op->_timingarray->[1]{'TimingStruct::_timingType'}