Carpenter
Carpenter

Reputation: 159

sorting an array on the first number found in each element

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
}

Upvotes: 0

Views: 1528

Answers (2)

Sinan &#220;n&#252;r
Sinan &#220;n&#252;r

Reputation: 118128

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;

Upvotes: 2

moritz
moritz

Reputation: 12842

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

Upvotes: 2

Related Questions