Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

hi wise monks i just wondering is there anyway to sort an array by a value contained within the array element
such as in the below result i want to have the array sorted by the available space on the volume.
host:volume available(MB) %free 149.153.130.11:/dev/hda3 10168 80% 149.153.130.11:/dev/hda2 84 90% 149.153.130.15:/dev/hda3 10168 80% 149.153.130.15:/dev/hda2 84 90%
where the out put is generated by
@lines = split(/\n/,$space{ $host }); shift @lines; foreach $disk_used (@lines) { $_ = $disk_used; if (/(\/\w+\/\w+)\s+\w+\s+\d+\s+\d+\s+(\d+)\s+(\d+)%/ ) { $free = 100-$3; push @out,"$host:$1 $2 $free%"; } }

Replies are listed 'Best First'.
Re: sort array by value in it
by dragonchild (Archbishop) on May 04, 2004 at 14:31 UTC
    I'd change your code slightly to make your array into an array of hashes.
    @lines = map { /(\/\w+\/\w+)\s+\w+\s+\d+\s+\d+\s+(\d+)\s+(\d+)%/; { volume => $1, available => $2, free => 100 - $3, } } split /\n/, $space{ $host }; # This is the sorting line! @lines = sort { $a->{available} <=> $b->{available} } @lines; push @out, "$host:$_->{volume} $_->{available} $_->{free}%" for +@lines;

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

      now i getting this funny looking warnings
      host:volume available(MB) %free Use of uninitialized value in subtraction (-) at freespace line 43. Use of uninitialized value in subtraction (-) at freespace line 43. Use of uninitialized value in numeric comparison (<=>) at freespace li +ne 50. Use of uninitialized value in numeric comparison (<=>) at freespace li +ne 50. Use of uninitialized value in numeric comparison (<=>) at freespace li +ne 50. Use of uninitialized value in numeric comparison (<=>) at freespace li +ne 50. Use of uninitialized value in concatenation (.) or string at freespace + line 52. Use of uninitialized value in concatenation (.) or string at freespace + line 52. Use of uninitialized value in concatenation (.) or string at freespace + line 52. Use of uninitialized value in concatenation (.) or string at freespace + line 52. 149.153.130.11:/dev/hda3 10168 80% 149.153.130.11:/dev/hda2 84 90% 1 149.153.130.11: 100% 2 149.153.130.11: 100% Use of uninitialized value in subtraction (-) at freespace line 43. Use of uninitialized value in subtraction (-) at freespace line 43. Use of uninitialized value in numeric comparison (<=>) at freespace li +ne 50. Use of uninitialized value in numeric comparison (<=>) at freespace li +ne 50. Use of uninitialized value in numeric comparison (<=>) at freespace li +ne 50. Use of uninitialized value in numeric comparison (<=>) at freespace li +ne 50. Use of uninitialized value in concatenation (.) or string at freespace + line 52. Use of uninitialized value in concatenation (.) or string at freespace + line 52. Use of uninitialized value in concatenation (.) or string at freespace + line 52. Use of uninitialized value in concatenation (.) or string at freespace + line 52. 149.153.130.23:/dev/hda3 10168 80% 149.153.130.23:/dev/hda2 84 90% 3 149.153.130.23: 100% 4 149.153.130.23: 100%
      also the lines numbered 1 2 3 4 above shouldnt be appearing
      where lines 43 50 and 52 are
      @lines = map { /(\/\w+\/\w+)\s+\w+\s+\d+\s+\d+\s+(\d+)\s+(\d+)%/; { 43 volume => $1, available => $2, free => 100 - $3, } } split /\n/, $space{ $host }; #This is the sorting line! 50 @lines = sort { $b->{available} <=> $a->{available} } @lines; 52 push @out, "$host:$_->{volume} $_->{available} $_->{free +}%" for @lines;
        Doesn't surprise me. Code posted on Perlmonks, unless otherwise stated, is completely untested. Remember - you didn't give us the whole script, so I have no idea what you plugged my snippet into.

        Additionally, did you even try to understand the snippet before plugging it into your code?

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

        I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re: sort array by value in it
by demerphq (Chancellor) on May 04, 2004 at 16:17 UTC

    This is a fairly common problem. The solution is to not use a string as a record, but to use a composite data type like an array or a hash as the record, sort that, and then construct your string from the record when its needed. The below is a version of this, where the string is actualyl part of the record. We sort the list of records, and then extract the strings from the resulting set.

    @lines = split(/\n/,$space{ $host }); shift @lines; my @recs; foreach $disk_used (@lines) { $_ = $disk_used; if (/(\/\w+\/\w+)\s+\w+\s+\d+\s+\d+\s+(\d+)\s+(\d+)%/ ) { $free = 100-$3; push @recs,[$host,$1,$2,$free,"$host:$1 $2 $free%" +]; } } @out=map { $_->[-1] } sort { $b->[3] <=> $a->[3] } @recs;

    HTH


    ---
    demerphq

      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi


      your code gave me back
      host:volume available(MB) %free 149.153.130.23:/dev/hda3 10168 80% 149.153.130.23:/dev/hda2 84 90%
      it didnt give me back the values for the first address i went to..????

      doh!!!!!


      forgot to move the declaration of the @recs outside the loop...
      tanx for all your help twas greatly appreciated...
Re: sort array by value in it
by Limbic~Region (Chancellor) on May 04, 2004 at 15:55 UTC
    Anonymous Monk,
    You mean something like this:
    #!/usr/bin/perl use strict; use warnings; my @data; while ( <DATA> ) { next if /^host:volume/; chomp; my @field = $_ =~ /^([^:]+):([^\s]+)\s+(\d+)\s+(\d+)/; push @data, \@field; } for ( sort { $data[$a]->[2] <=> $data[$b]->[2] } 0 .. $#data ) { print join "\t" , @{ $data[$_] }; print "\n"; } __DATA__ host:volume available(MB) %free 10.100.200.1:/dev/hda3 10168 80% 10.100.200.1:/dev/hda2 84 90% 10.100.200.2:/dev/hda3 10168 80% 10.100.200.2:/dev/hda2 84 90%
    Cheers - L~R
      what i mean is: below is the sorted output
      host:volume available(MB) %free 10.100.200.1:/dev/hda3 10168 80% 10.100.200.2:/dev/hda3 10168 80% 10.100.200.2:/dev/hda2 84 90% 10.100.200.1:/dev/hda2 84 90%

      i have an array containg multiple lines like this one
      10.100.200.1:/dev/hda3  10168 80%
      and i want to sort it on the value of the available field
        Anonymous Monk,
        Did you try the program? All you would have to do is swap $a and $b. I do not remember you saying you wanted it in descending order.

        Cheers - L~R