I have a hash with the following key/value pair
4 => model1
2 => model2
I want the following string created from the above hash
4 X model1 , 2 X model2
I tried the following
my %hash,
foreach my $keys (keys %hash) {
my $string = $string . join(' X ',$keys,$hash{$keys});
}
print $string;
What I get is
4 X model12Xmodel2
How can I accomplish the desired result 4 X model1 , 2 X model2?
You could do:
my %hash = (4 => "model1", 2 => "model2");
my $str = join(", ", map { "$_ X $hash{$_}" } keys %hash);
print $str;
Output:
4 X model1, 2 X model2
How it works:
map { expr } list evaluates expr for every item in list, and returns the list that contains all the results of these evaluations. Here, "$_ X $hash{$_}" is evaluated for each key of the hash, so the result is a list of key X value strings. The join takes care of putting the commas in between each of these strings.
Note that your hash is a bit unusual if you're storing (item,quantity) pairs. It would usually be the other way around:
my %hash = ("model1" => 4, "model2" => 2);
my $str = join(", ", map { "$hash{$_} X $_" } keys %hash);
because with your scheme, you can't store the same quantity for two different items in your hash.
You can also modify your loop as follows:
my #tokens =();
foreach my $key (keys %hash) {
push #tokens, $string . join(' X ',$key, $hash{$key});
}
print join(', ' , #tokens);
Related
I want to sort an array of hashes by the hashes key, how could I do that in Perl?
The structure is created like this :
push (#{$structure[$endpoint][1]}, \%temp_hash);
%temp_hash is a simple hash with key->value.
And now I want to sort that array by the hashes key, there is only one key->value in each hash... been fighting with it for 2 hours already and I gave up..
Try
#sorted = sort { (keys %$a)[0] cmp (keys %$b)[0] } #{$structure[$endpoint][1]};
This sorts the elements of the array (which are hash references) according to the first (only) key of each hash. If the keys are numeric use <=> instead.
Test code:
%a = ( 'a' => 1 );
%b = ( 'zz' => 2 );
%c = ( 'g' => 3);
#arr = (\%a, \%b, \%c);
print "Unsorted\n";
for (#arr)
{
printf "%s\n",((keys %$_)[0]);
}
#sorted = sort { (keys %$a)[0] cmp (keys %$b)[0] } #arr;
print "\nSorted\n";
for (#sorted)
{
printf "%s\n",((keys %$_)[0]);
}
Hi I an extreme novice and I need help on what I should type so that the unique character count is displayed based on what the user inputs from their keyboard
I already have it set up to show the character count in the string
Here is the Code:
#!C:\Strawberry\perl\bin\perl
use strict;
use warnings;
print "Input Username";
my $str = <>;
chomp ($str);
print "You have typed: $str\n";
my $str_length = length($str);
print "Total Characters = " . $str_length . "\n";
exit;
You can use this function to get what you need:
sub func($) { my ($str, %hash) = shift; $hash{$_}++ for split //, $str; (length $str, scalar keys %hash) }
and this if you need to get count of certain char:
sub uniq_ch_count($$) { my ($ch, $str, %hash) = #_; $hash{$_}++ for split //, $str; $hash{$ch} }
EXAMPLE 1:
my ($chars_count, $uniq_chars_count) = func('one two three four');
print $chars_count . " " . $uniq_chars_count . "\n";
OUTPUT:
18 10
EXAMPLE 2:
print uniq_ch_count('d', "asdjkasdjd sdfj d ") . " " . uniq_ch_count(' ', "asdjkasdjd sdfj d ") . "\n";
OUTPUT:
5
3
The simplest method would be to use a hash:
# split the string into an array of characters
my #chars = split //, $str;
# lists of values can be assigned to multiple indexes at once
# here we assign each character an empty value, but since hash
# keys are unique in nature, every subsequent assignment overwrites
# the first.
my %uniq;
#uniq{#chars} = ();
# next get the list of keys from the hash and treat that list as
# a scalar which gives you the count
my $count = scalar keys %uniq;
See: http://perldoc.perl.org/perldata.html#Slices
OK, so the magic keyword here - as far as Perl is concerned is 'unique'. Because that usually means a hash is the tool for the job.
In perl, a hash is a set of key-value pairs, which means it's great for counting numbers of unique items.
So if you take your string, and split it into characters:
my %count_of;
foreach my $character ( split ( '', $str ) ) {
$count_of{$character}++;
}
You can then print out %count_of:
foreach my $character ( keys %count_of ) {
print "$character = $count_of{$character}\n";
}
But because keys %count_of gives you an array containing each 'key' - one of the nice tricks in perl, is an array in a scalar context, is just a number representing the number of elements. So you can do:
print scalar keys %count_of, " unique characters in $str\n";
I have an array as follows:
#array = ('a:b','c:d','e:f:g','h:j');
How can I convert this into the following using grep and map?
%hash={a=>1,b=>1,c=>1,d=>1,e=>1,f=>1,h=>1,j=>1};
I've tried:
#arr;
foreach(#array){
#a = split ':' , $_;
push #arr,#a;
}
%hash = map {$_=>1} #arr;
but i am getting all the values i should get first two values of an individual array
Its very easy:
%hash = map {$_=>1} grep { defined $_ } map { (split /:/, $_)[0..1] } #array;
So, you split each array element with ":" delimiter, getting bigger array, take only 2 first values; then grep defined values and pass it to other map makng key-value pairs.
You have to ignore everything except first two elements after split,
my #arr;
foreach (#array){
#a = split ':', $_;
push #arr, #a[0,1];
}
my %hash = map {$_=>1} #arr;
Using map,
my %hash =
map { $_ => 1 }
map { (split /:/)[0,1] }
#array;
I think this should work though not elegent enough. I use a temporary array to hold the result of split and return the first two elements.
my %hash = map { $_ => 1 } map { my #t = split ':', $_; $t[0], $t[1] } #array;
This filters out g key
my %hash = map { map { $_ => 1; } (split /:/)[0,1]; } #array;
If I have a hash:
%hash = ("Dog",1,"Cat",2,"Mouse",3,"Fly",4);
How can I extract the first X elements of this hash. For example if I want the first 3 elements, %newhash would contain ("Dog",1,"Cat",2,"Mouse",3).
I'm working with large hashes (~ 8000 keys).
"first X elements of this hash" doesn't mean anything. First three elements in order by numeric value?
my %hash = ( 'Dog' => 1, 'Cat' => 2, 'Mouse' => 3, 'Fly' => 4 );
my #hashkeys = sort { $hash{$a} <=> $hash{$b} } keys %hash;
splice(#hashkeys, 3);
my %newhash;
#newhash{#hashkeys} = #hash{#hashkeys};
You might want to use something like this:
my %hash = ("Dog",1,"Cat",2,"Mouse",3,"Fly",4);
for ( (sort keys %hash)[0..2] ) {
say $hash{$_};
}
You should have an array 1st:
my %hash = ("Dog" => 1,"Cat"=>2,"Mouse"=>3,"Fly"=>4);
my #array;
foreach $value (sort {$hash{$a} <=> $hash{$b} }
keys %hash)
{
push(#array,{$value=>$hash{$value}});
}
#get range:
my #part=#array[0..2];
print part of result;
print $part[0]{'Cat'}."\n";
I would like to make the value the key, and the key the value. What is the best way to go about doing this?
Adapted from http://www.dreamincode.net/forums/topic/46400-swap-hash-values/:
Assuming your hash is stored in $hash:
while (($key, $value) = each %hash) {
$hash2{$value}=$key;
}
%hash=%hash2;
Seems like much more elegant solution can be achieved with reverse (http://www.misc-perl-info.com/perl-hashes.html#reverseph):
%nhash = reverse %hash;
Note that with reverse, duplicate values will be overwritten.
Use reverse:
use Data::Dumper;
my %hash = ('month', 'may', 'year', '2011');
print Dumper \%hash;
%hash = reverse %hash;
print Dumper \%hash;
As mentioned, the simplest is
my %inverse = reverse %original;
It "fails" if multiple elements have the same value. You could create an HoA to handle that situation.
my %inverse;
push #{ $inverse{ $original{$_} } }, $_ for keys %original;
So you want reverse keys & vals in a hash? So use reverse... ;)
%hash2 = reverse %hash;
reverting (k1 => v1, k2 => v2) - yield (v2=>k2, v1=>k1) - and that is what you want. ;)
my %orig_hash = (...);
my %new_hash;
%new_hash = map { $orig_hash{$_} => $_ } keys(%orig_hash);
The map-over-keys solution is more flexible. What if your value is not a simple value?
my %forward;
my %reverse;
#forward is built such that each key maps to a value that is a hash ref:
#{ a => 'something', b=> 'something else'}
%reverse = map { join(',', #{$_}{qw(a b)}) => $_ } keys %forward;
Here is a way to do it using Hash::MultiValue.
use experimental qw(postderef);
sub invert {
use Hash::MultiValue;
my $mvh = Hash::MultiValue->from_mixed(shift);
my $inverted;
$mvh->each( sub { push $inverted->{ $_[1] }->#* , $_[0] } ) ;
return $inverted;
}
To test this we can try the following:
my %test_hash = (
q => [qw/1 2 3 4/],
w => [qw/4 6 5 7/],
e => ["8"],
r => ["9"],
t => ["10"],
y => ["11"],
);
my $wow = invert(\%test_hash);
my $wow2 = invert($wow);
use DDP;
print "\n \%test_hash:\n\n" ;
p %test_hash;
print "\n \%test_hash inverted as:\n\n" ;
p $wow ;
# We need to sort the contents of the multi-value array reference
# for the is_deeply() comparison:
map {
$test_hash{$_} = [ sort { $a cmp $b || $a <=> $b } #{ $test_hash{$_} } ]
} keys %test_hash ;
map {
$wow2->{$_} = [ sort { $a cmp $b || $a <=> $b } #{ $wow2->{$_} } ]
} keys %$wow2 ;
use Test::More ;
is_deeply(\%test_hash, $wow2, "double inverted hash == original");
done_testing;
Addendum
Note that in order to pass the gimmicky test here, the invert() function relies on %test_hash having array references as values. To work around this if your hash values are not array references, you can "coerce" the regular/mixed hash into a multi-value hash thatHash::MultiValue can then bless into an object. However, this approach means even single values will appear as array references:
for ( keys %test_hash ) {
if ( ref $test_hash{$_} ne 'ARRAY' ) {
$test_hash{$_} = [ $test_hash{$_} ]
}
}
which is longhand for:
ref($_) or $_ = [ $_ ] for values %test_hash ;
This would only be needed to get the "round trip" test to pass.
Assuming all your values are simple and unique strings, here is one more easy way to do it.
%hash = ( ... );
#newhash{values %hash} = (keys %hash);
This is called a hash slice. Since you're using %newhash to produce a list of keys, you change the % to a #.
Unlike the reverse() method, this will insert the new keys and values in the same order as they were in the original hash. keys and values always return their values in the same order (as does each).
If you need more control over it, like sorting it so that duplicate values get the desired key, use two hash slices.
%hash = ( ... );
#newhash{ #hash{sort keys %hash} } = (sort keys %hash);