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

I have the following line of code in a script that I'm working on:

print map {/(.+)\./} sort(RetrieveIllustrationList($cl_userinput));

The RetrieveIllustrationList() is a subroutine that returns an array of file names. I use map to remove the file extension from each file name before displaying it:

File Name 1File Name 2File Name 3

I also want to add a line break (\n) to the end of each file name. Preferable I could do it without adding another line of code, but if I type:

print map {/(.+)\./ . "\n"} sort(RetrieveIllustrationList($cl_userinput));

the result is:

1 1 1

I understand why this is. Does anyone know an easy workaround?

Replies are listed 'Best First'.
Re: Using map to Add a Line Breaks to a List
by Aristotle (Chancellor) on Mar 05, 2005 at 05:54 UTC

    The direct fix:

    print map { /(.+)\./ && "$1\n" } sort RetrieveIllustrationList( $cl_us +erinput );

    The subtle fix (my recommendation):

    print map { /(.+)\./, "\n" } sort RetrieveIllustrationList( $cl_userin +put );

    Doing a lot of extra work with a dash of join:

    print join "\n", map( /(.+)\./, sort RetrieveIllustrationList( $cl_use +rinput ) ), '';

    Letting print do the work with an implicit version of the subtle fix (probably the most efficient, but caveat microoptimizer):

    local $, = "\n"; print map( /(.+)\./, sort RetrieveIllustrationList( $cl_userinput ) ), + '';

    Suboptimal variations with a double loop:

    print map "$_\n", map /(.+)\./, sort RetrieveIllustrationList( $cl_use +rinput ); print "$_\n" for map /(.+)\./, sort RetrieveIllustrationList( $cl_user +input );

    I think that's enough for tonight.

    Makeshifts last the longest.

      I like your recommended subtle fix. I like the elegant perlishness about it. I think I may edit my code to use it.

      I don't understand how it works though. Can someone explain?

        As !1 explained: a successful pattern match in list context returns a list of captured matches, or an empty list if it didn't match. That's why your own map { /(.+)\./ } works — map provides list context to the block, so the pattern match returns the captured text. What I did is simply unconditionally add a "\n" to that list. So if the list was

        "foo.txt", "bar.bmp", "baz.pdf"

        then the result is

        "foo", "\n", "bar", "\n", "baz", "\n"

        which prompts print to duly produce the desired result.

        Makeshifts last the longest.

        It's due to the capturing regex in list context. This returns a list of captured strings. Map then passes this list and the "\n" to print. Since $, = $\ = "", your output works the way it does. Of course, this will print blank lines if your filename doesn't contain a period.

Re: Using map to Add a Line Breaks to a List
by bobf (Monsignor) on Mar 05, 2005 at 05:27 UTC
    You're using the m// in scalar context, so it is returning true on success instead of your captured text (see perlop). Separate the two statements:
    print map { m/(.+)/; $1 . "\n" } sort @array;
    You could use a join in there, too.
Re: Using map to Add a Line Breaks to a List
by saintmike (Vicar) on Mar 05, 2005 at 05:22 UTC
    I bet you meant to say you don't understand why this is. Just in case: it's because the return value of /(.+)\./ in case of a match is a list with a single element: The item matched between the parentheses.

    On the other hand, the return value of /(.+)\./ . "\n" is "1\n" because the dot puts the regex in scalar context, in which case it returns true (1) or false ("").

    You could work around it by putting a map { "$_\n" } in front of your map.

Re: Using map to Add a Line Breaks to a List
by artist (Parson) on Mar 05, 2005 at 05:36 UTC
    @files = ('hello.txt', 'perl.bmp'); print join "\n", map { s/(.+)\..+$/$1/;$_} @files;
Re: Using map to Add a Line Break to a List
by goober99 (Scribe) on Mar 05, 2005 at 05:36 UTC
    Thank you for your replies. saintmike, I did understand why, I just didn't know an easy workaround, but your explanation was very clear.

    I went by the solution bobf and lidden suggested. I liked being able to do it without adding another map. I assume not having to loop again would make it a miniscule fraction of a millisecond faster.
Re: Using map to Add a Line Breaks to a List
by lidden (Curate) on Mar 05, 2005 at 05:32 UTC
    Untested but i think this should work:
    print map {/(.+)\./ ; $1 . "\n"} sort(RetrieveIllustrationList($cl_use +rinput));
Re: Using map to Add a Line Breaks to a List
by graff (Chancellor) on Mar 05, 2005 at 18:12 UTC
    How odd that no one has suggested this yet:
    print map { s/\..*/\n/; $_ } sort(RetrieveIllustrationList($cl_userinp +ut));
Re: Using map to Add a Line Breaks to a List
by iblech (Friar) on Mar 06, 2005 at 10:17 UTC

    print map { (/(.+)\./)[0] . "\n" } sort(RetrieveIllustrationList($cl_userinput));

    --Ingo