I have a hash like
key value
1 ababababab
11 cdcdcdcdcd
21 efefefefef
31 fgfgfgfgfg
41 ererererer
now I have a array[0]=5 array[1]= 22
How can i get the string from 5-22
abababababcdcdcdcdcdef
I plan use foreach to compare key with 5 and 22, but i don't know how to solve it.
my %hash = qw(
1 ababababab
11 cdcdcdcdcd
21 efefefefef
31 fgfgfgfgfg
41 ererererer
);
my #array = (5,22);
my $str = join "", map $hash{$_}, sort {$a <=> $b} keys %hash;
print
my $result = substr($str, $array[0]-1, $array[1]-$array[0]+1);
Why are you storing your data in a hash if you want it to work like a string? Just put it in a string:
my $string = "abababababcdcdcdcdcdefefefefeffgfgfgfgfgererererer";
Or if you need that hash to exist for another use you need for later, construct a string from it to use for this operation:
my $string = join "", map $hash{$_}, sort {$a <=> $b} keys %hash;
Then get the substring from position 5-22:
my $fragment = substr $string, 5, 17; # Arguments: string, start, length
I'm sure you can find a hacky way to do this with your hash, but that's not what hashes are made for and this will be far more optimal and readable.
First u need to track down the hash keypair values u need. For 5 and 22, u need $hash{1},$hash{11} and $hash{21}.. A small snippet
if($a=1;$a<=41;$a+10)
{
if($array[0] >=$a && $array[0] <=$a+10)
{
$starting_hash_key=$a;
}
if($array[1] >=$a && $array[1] <=$a+10)
{
$Ending_hash_key=$a;
}
}
Get all hashes in between the two hash.. Use substr function (http://perldoc.perl.org/functions/substr.html) to starting and ending hash to get the apt characters.
Related
I has a file that contain information like below
1:2=14
3:4=15
2:1=16
4:3=17
I would like to create a hash that treat key {1:2} same with key {2:1}
$hash{1:2} = $hash{2:1}
so when i print all element in $hash{1:2} or $hash{2:1} it give me both 14 and 16 as the result. Is it a simple way to do that?
Below are my planning to create the key for hash
for ( my $i = 0; $i <=$#file ; $i++) {
my $line = $file[$i];
my #key = split ("=",$line);
my $hash{$key[0]} = $key[1];
}
There is no built-in way to do that. If your keys follow the same pattern, you can wrap it in a function, then compute all keys that fit your algorithm and get the values.
my %hash = (
'1:2' => 14,
'3:4' => 15,
'2:1' => 16,
'4:3' => 17,
);
sub get_elements {
my ($key) = #_;
return $hash{$key}, $hash{reverse $key}; # or $key =~ s/(\d+):(\d+)/$2:$1/r
}
print join ' ', get_elements('1:2');
This uses string reverse, and obviously only works if the parts of the key are only one digit. If you can have numbers greater than 9 you will have to split the string and re-assemble it, or use a substitution to switch them around. My example uses the /r modifier, which needs Perl 5.14 or higher.
If you want to however build a data structure when reading your file that takes care of this automatically, you can do that too. Use an array reference instead of the simple values inside your hash, and assign the same reference to all keys that you want to be equal.
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my %hash;
while (my $line = <DATA>) {
chomp $line;
my ($key, $value) = split /=/, $line;
my ($left, $right) = split /:/, $key;
# $hash{$key} //= ( $hash{"$right:$left"} // [] ); # needs 5.10
$hash{$key} = ( $hash{"$right:$left"} || [] )
unless exists $hash{$key};
push #{ $hash{$key} }, $value;
}
say "#{ $hash{'1:2'} }";
say "#{ $hash{'2:1'} }";
print Dumper \%hash;
__DATA__
1:2=14
3:4=15
2:1=16
4:3=17
The output of this code is
14 16
14 16
The Data::Dumper structure looks like this, which explains that both keys 2:1 and 1:2 point to the same array reference. That means that if you push another value into one of them, it will end up in the other as well, because they are actually the same thing.
$VAR1 = {
'4:3' => [
'15',
'17'
],
'3:4' => $VAR1->{'4:3'},
'2:1' => [
'14',
'16'
],
'1:2' => $VAR1->{'2:1'}
};
The only downside of this is that you cannot get the order of the elements back. This data structure looses the knowledge that 1:2 had the value 14 and 2:1 had 16 initially. If you want 2:1 to output 16 14 this will not work.
So, for example i have array like that:
my #arr = (
"blabla\t23\t55",
"jkdcbx\t55\t89",
"jdxjcl\t88\t69",
......)
And i need to sort this array by second column after \t, without outer splits. Is it possible to do?
May be a more elegant way but this will work :
my #arr = ( "blabla\t23\t55", "jkdcbx\t55\t89", "jdxjcl\t88\t69");
for (sort {(split(/\t/,$a))[2] <=> (split(/\t/,$b))[2]} #arr) {
print "$_\n";
}
Update
I've just realised that your question may mean that you want to sort by the third column instead of the second
That would be done by using
my ($aa, $bb) = map { (split /\t/)[2] } $a, $b;
instead
output
blabla 23 55
jdxjcl 88 69
jkdcbx 55 89
I always prefer to use map to convert the values from the original data into the function that they should be sorted by
This program demonstrates
I assume you want the values sorted numerically? Unfortunately your example data is already sorted as you describe
use strict;
use warnings 'all';
use feature 'say';
my #arr = (
"blabla\t23\t55",
"jkdcbx\t55\t89",
"jdxjcl\t88\t69",
);
my #sorted = sort {
my ($aa, $bb) = map { (split /\t/)[1] } $a, $b;
$aa <=> $bb;
} #arr;
say for #sorted;
output
blabla 23 55
jkdcbx 55 89
jdxjcl 88 69
Try this
use warnings;
use strict;
no warnings "numeric";
my #arr = (
"blabla\t23\t55",
"jkdcbx\t85\t89",
"jdxjcl\t83\t69",
);
my #result = sort {$a=~s/^[^\t]*\t//r <=> $b=~s/^[^\t]*\t//r } #arr;
$, = "\n";
print #result,"\n";
I have used following technique with sort for to do it
Negation character class
Non-destructive modifier(-r) - perform non-destructive substitution and return the new value
And tured of the warning for numeric
The goal: I want to check if all entries in a hash are equal in some manner (here it's the count).
My nasty solution:
# initialize some fake data
my #list1 = (2, 3);
my #list2 = (1, 2);
my %hash = (12 => \#list1, 22 => \#list2);
# here starts the quest for the key
my $key;
foreach my $a (keys %hash) {
$key = $a;
}
# some $key is set
# here starts the actual comparision
my $count = scalar(#{%hash{$key}});
foreach my $arr_ref (%hash) {
my $actcount = scalar(#$arr_ref);
print "some warning" if $actcount != $count;
}
I know I could also store the size in the first iteration of the loop, then I would not need to get the key in advance. But it will cause me a conditional statement in eacht iteration. Thus I like to avoid it.
The question:
What is a proper way to get a key from the hash?
Addition:
There should be something possible like
(keys %hash)[0]
my $key = (keys %hash)[0] should work, or you can force list context by enclosing the scalar you assign to in parentheses:
my ($key) = keys %hash;
Another way would be to use each in scalar context:
my $key = each %hash;
In the testing loop, you are only interested in values, so don't iterate over the keys, too:
for my $arr_ref (values %hash) {
I'm looking for help sorting an array where each element is made up of "a number, then a string, then a number". I would like to sort on the first number part of the array elements, descending (so that I list the higher numbers first), while also listing the text etc.
am still a beginner so alternatives to the below are also welcome
use strict;
use warnings;
my #arr = map {int( rand(49) + 1) } ( 1..100 ); # build an array of 100 random numbers between 1 and 49
my #count2;
foreach my $i (1..49) {
my #count = join(',', #arr) =~ m/$i,/g; # maybe try to make a string only once then search trough it... ???
my $count1 = scalar(#count); # I want this $count1 to be the number of times each of the numbers($i) was found within the string/array.
push(#count2, $count1 ." times for ". $i); # pushing a "number then text and a number / scalar, string, scalar" to an array.
}
#for (#count2) {print "$_\n";}
# try to add up all numbers in the first coloum to make sure they == 100
#sort #count2 and print the top 7
#count2 = sort {$b <=> $a} #count2; # try to stop printout of this, or sort on =~ m/^anumber/ ??? or just on the first one or two \d
foreach my $i (0..6) {
print $count2[$i] ."\n"; # seems to be sorted right anyway
}
First, store your data in an array, not in a string:
# inside the first loop, replace your line with the push() with this one:
push(#count2, [$count1, $i];
Then you can easily sort by the first element of each subarray:
my #sorted = sort { $b->[0] <=> $a->[0] } #count2;
And when you print it, construct the string:
printf "%d times for %d\n", $sorted[$i][0], $sorted[$i][1];
See also: http://perldoc.perl.org/perlreftut.html, perlfaq4
Taking your requirements as is. You're probably better off not embedding count information in a string. However, I'll take it as a learning exercise.
Note, I am trading memory for brevity and likely speed by using a hash to do the counting.
However, the sort could be optimized by using a Schwartzian Transform.
EDIT: Create results array using only numbers that were drawn
#!/usr/bin/perl
use strict; use warnings;
my #arr = map {int( rand(49) + 1) } ( 1..100 );
my %counts;
++$counts{$_} for #arr;
my #result = map sprintf('%d times for %d', $counts{$_}, $_),
sort {$counts{$a} <=> $counts{$b}} keys %counts;
print "$_\n" for #result;
However, I'd probably have done something like this:
#!/usr/bin/perl
use strict; use warnings;
use YAML;
my #arr;
$#arr = 99; #initialize #arr capacity to 100 elements
my %counts;
for my $i (0 .. 99) {
my $n = int(rand(49) + 1); # pick a number
$arr[ $i ] = $n; # store it
++$counts{ $n }; # update count
}
# sort keys according to counts, keys of %counts has only the numbers drawn
# for each number drawn, create an anonymous array ref where the first element
# is the number drawn, and the second element is the number of times it was drawn
# and put it in the #result array
my #result = map [$_, $counts{$_}],
sort {$counts{$a} <=> $counts{$b} }
keys %counts;
print Dump \#result;
I have an array in Perl:
my #my_array = ("one","two","three","two","three");
How do I remove the duplicates from the array?
You can do something like this as demonstrated in perlfaq4:
sub uniq {
my %seen;
grep !$seen{$_}++, #_;
}
my #array = qw(one two three two three);
my #filtered = uniq(#array);
print "#filtered\n";
Outputs:
one two three
If you want to use a module, try the uniq function from List::MoreUtils
The Perl documentation comes with a nice collection of FAQs. Your question is frequently asked:
% perldoc -q duplicate
The answer, copy and pasted from the output of the command above, appears below:
Found in /usr/local/lib/perl5/5.10.0/pods/perlfaq4.pod
How can I remove duplicate elements from a list or array?
(contributed by brian d foy)
Use a hash. When you think the words "unique" or "duplicated", think
"hash keys".
If you don't care about the order of the elements, you could just create the hash then extract the keys. It's not important how you create that hash: just that you use "keys" to get the unique elements.
my %hash = map { $_, 1 } #array;
# or a hash slice: #hash{ #array } = ();
# or a foreach: $hash{$_} = 1 foreach ( #array );
my #unique = keys %hash;
If you want to use a module, try the "uniq" function from
"List::MoreUtils". In list context it returns the unique elements, preserving their order in the list. In scalar context, it returns the number of unique elements.
use List::MoreUtils qw(uniq);
my #unique = uniq( 1, 2, 3, 4, 4, 5, 6, 5, 7 ); # 1,2,3,4,5,6,7
my $unique = uniq( 1, 2, 3, 4, 4, 5, 6, 5, 7 ); # 7
You can also go through each element and skip the ones you've seen
before. Use a hash to keep track. The first time the loop sees an
element, that element has no key in %Seen. The "next" statement creates
the key and immediately uses its value, which is "undef", so the loop
continues to the "push" and increments the value for that key. The next
time the loop sees that same element, its key exists in the hash and
the value for that key is true (since it's not 0 or "undef"), so the
next skips that iteration and the loop goes to the next element.
my #unique = ();
my %seen = ();
foreach my $elem ( #array )
{
next if $seen{ $elem }++;
push #unique, $elem;
}
You can write this more briefly using a grep, which does the same thing.
my %seen = ();
my #unique = grep { ! $seen{ $_ }++ } #array;
Install List::MoreUtils from CPAN
Then in your code:
use strict;
use warnings;
use List::MoreUtils qw(uniq);
my #dup_list = qw(1 1 1 2 3 4 4);
my #uniq_list = uniq(#dup_list);
My usual way of doing this is:
my %unique = ();
foreach my $item (#myarray)
{
$unique{$item} ++;
}
my #myuniquearray = keys %unique;
If you use a hash and add the items to the hash. You also have the bonus of knowing how many times each item appears in the list.
Can be done with a simple Perl one-liner.
my #in=qw(1 3 4 6 2 4 3 2 6 3 2 3 4 4 3 2 5 5 32 3); #Sample data
my #out=keys %{{ map{$_=>1}#in}}; # Perform PFM
print join ' ', sort{$a<=>$b} #out;# Print data back out sorted and in order.
The PFM block does this:
Data in #in is fed into map. map builds an anonymous hash. keys are extracted from the hash and feed into #out
Method 1: Use a hash
Logic: A hash can have only unique keys, so iterate over array, assign any value to each element of array, keeping element as key of that hash. Return keys of the hash, its your unique array.
my #unique = keys {map {$_ => 1} #array};
Method 2: Extension of method 1 for reusability
Better to make a subroutine if we are supposed to use this functionality multiple times in our code.
sub get_unique {
my %seen;
grep !$seen{$_}++, #_;
}
my #unique = get_unique(#array);
Method 3: Use module List::MoreUtils
use List::MoreUtils qw(uniq);
my #unique = uniq(#array);
The variable #array is the list with duplicate elements
%seen=();
#unique = grep { ! $seen{$_} ++ } #array;
That last one was pretty good. I'd just tweak it a bit:
my #arr;
my #uniqarr;
foreach my $var ( #arr ){
if ( ! grep( /$var/, #uniqarr ) ){
push( #uniqarr, $var );
}
}
I think this is probably the most readable way to do it.
Previous answers pretty much summarize the possible ways of accomplishing this task.
However, I suggest a modification for those who don't care about counting the duplicates, but do care about order.
my #record = qw( yeah I mean uh right right uh yeah so well right I maybe );
my %record;
print grep !$record{$_} && ++$record{$_}, #record;
Note that the previously suggested grep !$seen{$_}++ ... increments $seen{$_} before negating, so the increment occurs regardless of whether it has already been %seen or not. The above, however, short-circuits when $record{$_} is true, leaving what's been heard once 'off the %record'.
You could also go for this ridiculousness, which takes advantage of autovivification and existence of hash keys:
...
grep !(exists $record{$_} || undef $record{$_}), #record;
That, however, might lead to some confusion.
And if you care about neither order or duplicate count, you could for another hack using hash slices and the trick I just mentioned:
...
undef #record{#record};
keys %record; # your record, now probably scrambled but at least deduped
Try this, seems the uniq function needs a sorted list to work properly.
use strict;
# Helper function to remove duplicates in a list.
sub uniq {
my %seen;
grep !$seen{$_}++, #_;
}
my #teststrings = ("one", "two", "three", "one");
my #filtered = uniq #teststrings;
print "uniq: #filtered\n";
my #sorted = sort #teststrings;
print "sort: #sorted\n";
my #sortedfiltered = uniq sort #teststrings;
print "uniq sort : #sortedfiltered\n";
Using concept of unique hash keys :
my #array = ("a","b","c","b","a","d","c","a","d");
my %hash = map { $_ => 1 } #array;
my #unique = keys %hash;
print "#unique","\n";
Output:
a c b d