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

Hello All,

I have an array which looks like this:

my @ar1 = (text1.txt, text1a.txt, text2.txt, text54.txt,...text1g.txt,...text54f.txt,...);

I would like to have always the last version of my names inside the array. The result should look like this:

my @ar2 = (text2.txt,...text1g.txt,...text54f.txt,...);

I would appreciate some help. Thank you in advance.

Replies are listed 'Best First'.
Re: Sorting array (updated)
by haukex (Archbishop) on Dec 05, 2016 at 14:58 UTC

    Hi negativ_m,

    This is a frequently asked question, see for example the recent thread Sorting text-number values for some solutions as well as links to other places like How do I do a natural sort on an array? that give even more possible solutions. One possible module to help you is Sort::Key.

    Update: I just realized your sorting rules appear to be unclear: text1g.txt comes after text2.txt but before text54f.txt?

    Update 2: As Eily noted, it does seem like you might want to filter the array instead of or in addition to sorting it. Please see How do I post a question effectively?, at the very least you'd have to describe the algorithm for selection of the "latest version" as well as give enough test cases so we don't have to guess.

    Hope this helps,
    -- Hauke D

Re: Sorting array
by Eily (Monsignor) on Dec 05, 2016 at 15:13 UTC

    Hello negativ_m. FYI sort in computing is most of the time used to mean order (eg: from smallest to bigest). Here it rather looks like you want to select some elements out of your list. In your case, you want an unique file for each number. In perl, there's one thing that does uniqueness well: hashes. You could do something like:

    use Data::Dumper; my %uniq; for my $file (@ar1) { my ($number,) = $file =~ /(\d+)/; $uniq{$number} = $file; } print Dumper \%uniq; my @values = values %uniq;
    This will not do exactly what you want, you'll have to add a condition to only replace the value in $uniq{$number} if the current file has a higher letter than the one already stored. You can compare two strings with gt and lt. eg: "text2g.txt" gt "text2a.txt".

Re: Sorting array
by tybalt89 (Monsignor) on Dec 05, 2016 at 15:27 UTC
    #!/usr/bin/perl # http://perlmonks.org/?node_id=1177217 use strict; use warnings; my @ar1 = qw(text1.txt text1a.txt text2.txt text54.txt text1g.txt text54f.txt); my %latest; @latest{ /^text(\d+)\D/ } = $_ for sort @ar1; my @lastversion = @latest{sort {$a <=> $b} keys %latest}; print "@lastversion\n";
Re: Sorting array
by tybalt89 (Monsignor) on Dec 05, 2016 at 19:19 UTC

    If you don't care that the output array is in numerical order (like your desired output), it can be a one-liner - hehehe

    #!/usr/bin/perl # http://perlmonks.org/?node_id=1177217 use strict; use warnings; my @ar1 = qw(text1.txt text1a.txt text2.txt text54.txt text1g.txt text54f.txt); my @lastversion = values %{{ map { /(\d+)/, $_ } sort @ar1 }}; print "@lastversion\n";

      Hello,

      First I want to thank you a lot for this elegant solution. I tested your code on a real life case and for some reason it doesn't work.

      Here is the code:

      #!/usr/bin/perl -w use strict; use warnings; use Data::Dumper; my @ar1=qw(F40_I_VBBG_RI50-FMVSS301n-2SR424M-HCH-D_01EK101.a3db F40_I_VBBG_RI50-FMVSS301n-2SR424M-HCH-D_01EK101a.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101g.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101k.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101.a3db ); #my @lastversion = values %{{ map { /EK(\d+)\w?/, $_ } sort @ar2 }}; my %latest; @latest{ /EK(\d+)\D/ } = $_ for sort @ar1; my @lastversion = @latest{sort {$a <=> $b} keys %latest}; foreach (@lastversion) { #print "@lastversion\n"; print "$_\n"; } #print Dumper(\%latest);

      The correct output should be this:

      F40_I_VBBG_RI50-FMVSS301n-2SR424M-HCH-D_01EK101a.a3db

      F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101k.a3db

      After i run the script i just get the first name:

      F40_I_VBBG_RI50-FMVSS301n-2SR424M-HCH-D_01EK101a.a3db.

      I really can't tell what the problem is. I am asking again for your help.

      Thank you in advance.

        I really can't tell what the problem is

        This regex /EK(\d+)\D/ captures only the numerics after the EK which is 101 for all the records. Expand the regex to capture the full key (less the last suffix letter if present).

        #!/usr/bin/perl -w use strict; use warnings; my @ar1 = qw( F40_I_VBBG_RI50-FMVSS301n-2SR424M-HCH-D_01EK101.a3db F40_I_VBBG_RI50-FMVSS301n-2SR424M-HCH-D_01EK101a.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101g.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101k.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101.a3db ); my %latest; for (sort @ar1){ if (/(.*?)[a-z]?\.a3db/){ $latest{$1} = $_; } else { warn "No match for $_"; } } print "$_\n" for reverse sort values %latest;
        poj

        I really can't tell what the problem is.
        The problem is you gave fake data for your problem instead of real data.

        fake data => fake solution

        (not to mention wasting time).

        #!/usr/bin/perl -l # http://perlmonks.org/?node_id=1177217 use strict; use warnings; my @ar1=qw(F40_I_VBBG_RI50-FMVSS301n-2SR424M-HCH-D_01EK101.a3db F40_I_VBBG_RI50-FMVSS301n-2SR424M-HCH-D_01EK101a.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101g.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101k.a3db F40_I_VBBG_LR50-ECR17p-2SR424M-HCH-D_01EK101.a3db ); print for values %{{ map { /(.*\d)[a-z]?\./, $_ } sort @ar1 }};