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

Note: After composing this question I realized I could use just one array to store references to anonymous arrays containing the three fields I'm interested in. Still, I'm curious to learn one or more ways to accomplish my original question.

In the code below, what is the "Perl way" to simultaneously push $1, $2, and $3 to the three arrays @ids, @urls, and @titles? That is, I'd like to learn a way to do this with just one assignment (unless the code would violate the Simplicity & Clarity (from "The Practice of Programming" by Kernighan and Pike).

Caveat: In actual use this code will be extracting about 20 sets of url/id/title from a scalar variable holding the contents of an approx. 250kb HTML file fetched via LWP. Which means: the regex will include the 'g' modifier so the 'while' loop won't be present.

#! perl -w use strict; use Data::Dump qw(dump); my (@urls, @ids, @titles) = (); while (<DATA>) { my ($url, $id, $title) = ($_ =~ m|<h6 class="project-title"><a hre +f="(/projects/(\d+)/.+?\?ref=discovery)" target="">(.+?)</a></h6>|o ) +; push @urls, $url; push @ids, $id; push @titles, $title; } dump @urls, @ids, @titles; __END__ <h6 class="project-title"><a href="/projects/1956727289/how-to-build-a +-spaceship?ref=discovery" target="">How To Build A Spaceship</a></h6> +<p class="project-byline">Rohan Sinha</p>

Thank you for your time.

Searched for donut and crumpit. Found donate and stumbit instead.
  • Comment on What is Perl way to simultaneously assign to three separate arrays?
  • Download Code

Replies are listed 'Best First'.
Re: What is Perl way to simultaneously assign to three separate arrays?
by LanX (Saint) on Feb 15, 2015 at 03:03 UTC
    > what is the "Perl way" to simultaneously push $1, $2, and $3 to the three arrays @ids, @urls, and @titles?

    None, any golfing here would be awkward for just 3 arrays and too hard to maintain.

    Truth is - like you already realized - that it doesn't make sense to have three different arrays.

    This

    my ($url, $id, $title) = ($_ =~ m|<h6 class="project-title"><a hre +f="(/proje +cts/(\d+)/.+?\?ref=discovery)" target="">(.+?)</a></h6>|o ); push @project, [$url, $id, $title];

    already does the trick.

    ( IMHO even better $project{$id} = [$url,$title] )

    Please note that I didn't try to replace ($url, $id, $title) with @matches cause you'd loose self-documenting names.

    But if you really want to condense it further try

    push @project, [ m|<h6 class="project-title"> ... yadda ... </h6>| +o ];

    HTH!

    Cheers Rolf

    PS: Je suis Charlie!

    PS: As a side note, better dump references

    dump \@urls, \@ids, \@titles;

      Thank you for your comments, esp. named captures and proper use of dump.

      When I run the code, the array ends up having nine separate scalar elements instead of three references to anonymous arrays containing three scalar elements each

      Here's what I'm currently getting:

      [ "/projects/2138137193/iblab?ref=discovery", 2138137193, "iBlab", "/projects/2060538158/nz-hosting?ref=discovery", 2060538158, "NZ Hosting", "/projects/1956727289/how-to-build-a-spaceship?ref=discovery", 1956727289, "How To Build A Spaceship", ]

      Here's what I want:

      [ [ "/projects/2138137193/iblab?ref=discovery", 2138137193, "iBlab" ], [ "/projects/2060538158/nz-hosting?ref=discovery", 2060538158, "NZ Hosting" ], [ "/projects/1956727289/how-to-build-a-spaceship?ref=discovery", 1956727289, "How To Build A Spaceship" ] ]

      How is this possible given the constraint that the entire HTML content is a scalar (obtained via LWP)?

      (I realize I can "post-process" the @projects array but I'd like to know if everything can be done in a single assignment without any munging afterward.)

      #! perl -w use strict; use Data::Dump qw(dump); my $html = '<h6 class="project-title"><a href="/projects +/2138137193/iblab?ref=discovery" target="">iBlab</a></h6><h6 class="p +roject-title"><a href="/projects/2060538158/nz-hosting?ref=discovery" + target="">NZ Hosting</a></h6><h6 class="project-title"><a href="/pro +jects/1956727289/how-to-build-a-spaceship?ref=discovery" target="">Ho +w To Build A Spaceship</a></h6>'; my @projects = $html =~ m|<h6 class="project-title"><a href="(/project +s/(\d+)/.+?\?ref=discovery)" target="">(.+?)</a></h6>|g; dump \@projects;
      Searched for donut and crumpit. Found donate and stumbit instead.
        > I'd like to know if everything can be done in a single assignment without any munging afterward.)

        No, you are using /g in list context which swallows the whole file now and returns a flat list.

        You need an external loop to process the 3 groups

        update

        With a post while you can have the loop in the same line

         push @res, [ $1,$2,$3 ] while m/.../g

        In the OP you were originally looping with $_ , that's confusing.

        you should rather be concerned about maintainability than shortness.

        Cheers Rolf

        PS: Je suis Charlie!

        Here's what I came up with to "post-process" the array to get what I wanted:

        #! /usr/bin/perl -w use strict; use Data::Dump qw(dump); my @src = ( "/projects/2138137193/iblab?ref=discovery", 2138137193, "iBlab", "/projects/2060538158/nz-hosting?ref=discovery", 2060538158, "NZ Hosting", "/projects/1956727289/how-to-build-a-spaceship?ref=discovery", 1956727289, "How To Build A Spaceship" ); my @dst = (); my $by = 3; my $len = @src / $by; die "Not an integral multiple\n" unless ( 0 == @src % $by ); for (my $i = 0; $i < $len; $i++) { push @dst, [ splice @src, 0, 3 ]; } dump \@dst;

        The output:

        [ [ "/projects/2138137193/iblab?ref=discovery", 2138137193, "iBlab", ], [ "/projects/2060538158/nz-hosting?ref=discovery", 2060538158, "NZ Hosting", ], [ "/projects/1956727289/how-to-build-a-spaceship?ref=discovery", 1956727289, "How To Build A Spaceship", ], ]

        I don't know why but lately I've been in a mood to "do it in one statement" or "do it in place". So I have to ask: "Can this be done in-place with just one array?"

        Searched for donut and crumpit. Found donate and stumbit instead.
Re: What is Perl way to simultaneously assign to three separate arrays?
by LanX (Saint) on Feb 15, 2015 at 03:30 UTC
    another way to improve readability are named captures, here an approach to build a hash of hashes.

    DB<152> %project =() DB<153> $_="A B C" => "A B C" DB<154> m/(?<url>\w) (?<id>\w) (?<title>\w)/; $project{ $+{id} } + = { %+ } => { id => "B", title => "C", url => "A" } DB<155> \%project => { B => { id => "B", title => "C", url => "A" } }

    (line 154 should be two, but the debugger can't keep matches alive after quitting eval)

    if you prefer AoAs just do push @projects, [ values %+ ] , ... and so on.

    Cheers Rolf

    PS: Je suis Charlie!