Consider:
use warnings;
my #a = (1, 11, 3, 5, 21, 9, 10);
my #b = sort #a;
print "#b";
Output: 1 10 11 21 3 5 9
Codepad link: http://codepad.org/Fvhcf3eP
I guess the sort function is not taking the array's elements as an integer. That is why the output is not:
1 3 5 9 10 11 21
Is it?
How can I get the above result as output?
The default implementation of Perl's sort function is to sort values as strings. To perform numerical sorting:
my #a = sort {$a <=> $b} #b;
The linked page shows other examples of how to sort case-insensitively, in reverse order (descending), and so on.
You can create explicit subroutines to prevent duplication:
sub byord { $a <=> $b };
...
#a = sort byord #b;
This is functionally equivalent to the first example using an anonymous subroutine.
You are correct. So just tell Perl to treat it as an integer like below.
File foop.pl
use warnings;
my #a = (1, 11, 3, 5, 21, 9, 10);
my #b = sort {$a <=> $b} #a;
print "#b";
Run
perl foop.pl
1 3 5 9 10 11 21
Provide a custom comparison function (comparing numerically):
sort {$a <=> $b} #array;
Here is a numerical sort:
#sorted = sort { $a <=> $b } #not_sorted
#b = sort { $a <=> $b } #a;
Is numerical
Use the spaceship operator: sort { $a <=> $b } #a
Guessing is the wrong approach. If you don't understand sort, look it up: sort
my #b = sort{$a <=> $b} #a;
Related
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.
I have the following script:
use strict;
use List::MoreUtils qw/uniq/;
use Data::Dumper;
my #x = (3,2);
my #y = (4,3);
print "unique results \n";
print Dumper([uniq(#x,#y)]);
print "sorted unique results\n";
print Dumper([sort uniq(#x,#y)]);
The output is
unique results
$VAR1 = [
3,
2,
4
];
sorted unique results
$VAR1 = [
2,
3,
3,
4
];
So it looks that sort does not work with uniq.
I did not understand why.
I ran the perl script with -MO=Deparse and got
use List::MoreUtils ('uniq');
use Data::Dumper;
use strict 'refs';
my(#x) = (3, 2);
my(#y) = (4, 3);
print "unique results \n";
print Dumper([uniq(#x, #y)]);
print "sorted unique results\n";
print Dumper([(sort uniq #x, #y)]);
My interpretation is that perl decided to remove the parentheses from uniq(#x,#y) and using uniq as a function of sort.
Why did perl decide to do it?
How can i avoid these and similar pitfalls?
Thanks,
David
The sort builtin accepts a subroutine name or block as first argument which is passed two items. It then must return a number which determines the order between the items. These snippets all do the same:
use feature 'say';
my #letters = qw/a c a d b/;
say "== 1 ==";
say for sort #letters;
say "== 2 ==";
say for sort { $a cmp $b } #letters;
say "== 3 ==";
sub func1 { $a cmp $b }
say for sort func1 #letters;
say "== 4 ==";
sub func2 ($$) { $_[0] cmp $_[1] } # special case for $$ prototype
say for sort func2 #letters;
Notice that there isn't any comma between the function name and the list, and note that the parens in Perl are primarly used to determine precedence – sort func1 #letters and sort func1 (#letters) are the same, and neither executes func1(#letters).
To disambiguate, place a + before the function name:
sort +uniq #letters;
To avoid such unexpected behaviour, the best solution is to read the docs when you aren't sure how a certain builtin behaves – unfortunately, many have some special parsing rules.
You could put parenthesis arround the uniq fonction:
print Dumper([sort (uniq(#x,#y))]);
output:
$VAR1 = [
2,
3,
4
];
I have a requirement to find out the minimum value that is occurring maximum times in the array .I have store those values in other array .
my #arr=(1,2,3,4,1,3,4,1);
1 is the minimum value that is occurring maximum times.
If there are two or more elements occurring same number of times, smaller is preferred:
my #arr=(1,2,3,4,1,3,4,1);
my %seen;
$seen{$_}++ for #arr;
my ($min_val) = sort { $seen{$b} <=> $seen{$a} || $a <=> $b } keys %seen;
print "$min_val\n";
You can use a hash to count the occurrences of each number. The most frequent numbers can be found as having the frequence equal to the max of the frequences, the minimum among them can be found by min, both min and max come from List::Util.
#!/usr/bin/perl
use warnings;
use strict;
use List::Util qw(min max);
my #arr = (1, 2, 3, 4, 1, 3, 4, 1);
my %occurrences;
$occurrences{$_}++ for #arr;
my $max_freq = max(values %occurrences);
print min(grep $max_freq == $occurrences{$_}, keys %occurrences);
Use this it will work perfect for you
my #arr=(1, 2, 3, 4, 1, 3, 4, 1);
my %count;
foreach (#arr){
$count{$_}++;
}
my ($min_by_value) = sort { $a <=> $b} keys %count;
my ($max_by_count) = sort { $count{$b} <=> $count{$a} } keys %count;
my $max =
($count{$min_by_value} >= $count{$max_by_count}) ? $min_by_value : $max_by_count;
print "minimum value max times = $max\n";
Anyone have any idea how to sort number by length?
Ex : (11,111,122,12,2,13,21,15,211,22,213,2004)
I wanted the sorted array to be:
11
12
13
15
111
122
2
21
22
213
2004
The desired output seems to indicate you don't just want to sort by the number of digits, but rather first sort by the first digit and then by the length.
The desired output you show omits 211, so I just put it where it belonged according to my understanding.
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
my #source = (11, 111, 122, 12, 2, 13, 21, 15, 211, 22, 213, 2004);
my #desired = (11, 12, 13, 15, 111, 122, 2, 21, 22, 211, 213, 2004);
my #sorted =sort {
substr($a, 0, 1) <=> substr($b, 0, 1) ||
length($a) <=> length($b) ||
$a <=> $b # thanks #ikegami
} #source;
is_deeply(\#sorted, \#desired, 'Sorted and desired are the same');
my #sorted =
sort { substr($a,0,1) <=> substr($b,0,1) || $a <=> $b }
#unsorted;
gives the order you requested. Or maybe you want
my #sorted =
sort { substr($a,0,1) <=> substr($b,0,1)
|| length($a) <=> length($b)
|| $a <=> $b }
#unsorted;
If 211 wasn't missing from the output you provided, I could would tell you which one you want.
Consider a so-called Schwartzian transform, which avoids recomputing the sort keys by temporarily associating them with the input items:
my #sorted =
map { $_->[0] }
sort { $a->[1] cmp $b->[1] or $a->[0] <=> $b->[0] }
map { [ $_ => sprintf "%.1s%08x", $_, length ] }
#source;
This is provided by List::UtilsBy::sort_by
use List::UtilsBy qw( sort_by );
my #sorted = sort_by { sprintf "%.1s%08x", $_, length } #source;
It's much the same as the Schwartzian Transform solutions others have suggested, but wrapped in a neat abstraction.
I'm looking for a logical (not additional module) to sort by such format. I have a list of strings which looks like:
asdadasBBBsfasdasdas-0112
asdanfnfnfnfnf222ads-1210
etc.
I cant just sort by the numbers, because, for instance: 812 > 113 (812 = August 2012, 113 = January 2013, so its incorrect)
any good strategy??
thanks,
A schwartzian transform would be a huge waste here. This similar construct whose name I can never remember would be way better.
my #sorted =
map substr($_, 4),
sort
map substr($_, -2) . substr($_, -4, 2) . $_,
#unsorted;
Using the match operator instead of substr:
my #sorted =
map substr($_, 4),
sort
map { /(..)(..)\z/s; $2.$1.$_ }
#unsorted;
How about Schwartzian transform:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dump qw(dump);
my #list = (
'asdadasBBBsfasdasdas-0112',
'asdanfnfnfnfnf222ads-1210',
'asdanfnfnfnfnf222ads-1211',
'asdanfnfnfnfnf222ads-1010',
'asdanfnfnfnfnf222ads-1011',
);
my #sorted =
map { $_->[0] }
sort { $a->[1] <=> $b->[1] or $a->[2] <=> $b->[2] }
map { /-(\d\d)(\d\d)$/; [$_, $2, $1] } #list;
dump #sorted;
output:
(
"asdanfnfnfnfnf222ads-1010",
"asdanfnfnfnfnf222ads-1210",
"asdanfnfnfnfnf222ads-1011",
"asdanfnfnfnfnf222ads-1211",
"asdadasBBBsfasdasdas-0112",
)
Use a sorting function that looks at the year first, and then the date:
sub mmyy_sorter {
my $a_yy = substr($a, -2);
my $b_yy = substr($b, -2);
my $a_mm = substr($a, -4, 2);
my $b_mm = substr($b, -4, 2);
return ($a_yy cmp $b_yy) || ($a_mm cmp $b_mm);
}
my #sorted = sort mmyy_sorter #myarray;
NB: this is technically not as efficient as it could be as it has to re-calculate the month and year subfields for every comparison, not just once for each item in the array.
It would also be possible to take advantage of Perl's automatic type conversion and use the <=> operator in place of cmp, since all of the values actually represent numbers.
What about remake it to months? For example:
812 = 12 * 12 + 8
113 = 13 * 12 + 1
You can turn years into months and it will be good. For selecting numbers you can use regex.
Thanks to #M42 for the sample data.
use strict;
use warnings;
use feature 'say';
my #list = (
'asdadasBBBsfasdasdas-0112',
'asdanfnfnfnfnf222ads-1210',
'asdanfnfnfnfnf222ads-1211',
'asdanfnfnfnfnf222ads-1010',
'asdanfnfnfnfnf222ads-1011',
);
my #sorted = sort {
my ($aa, $bb) = map { /(..)(..)\z/ and $2.$1 } $a, $b;
$aa <=> $bb;
} #list;
say for #sorted;
output
asdanfnfnfnfnf222ads-1010
asdanfnfnfnfnf222ads-1210
asdanfnfnfnfnf222ads-1011
asdanfnfnfnfnf222ads-1211
asdadasBBBsfasdasdas-0112