in reply to Fence vs. Posts

This feels like an XY Problem to me. Why create lists with a bunch of duplicates, when you can just walk through @trip, looking at the current item and the next? Also, NetWallah made an excellent point about graphs - although in this case, if your "trips" are always linear, you don't really need a graph. I don't quite understand your question about the @states array - why not build the hash with city/state information first? So if you could explain the background of your question some more, we could probably suggest even better solutions.

Here's how I might have approached it - as you can see, no arrays with duplicate elements involved. In the "alternative" I go a step further and make the elements of @trip references to the anonymous hashes. So that I don't lose the city name, I store it in those hashes as well. This has the advantage that you know beforehand that all your cities are spelled correctly, and all the city information is available via the elements of the @trip array (notice how I no longer need %cities in the loop).

use warnings; use strict; use Data::Dump; my @trip = ("Chicago", "Saint Looey", "Joplin", "OKC", "Amarillo", "Gallup", "Flagstaff", "Winona", "Kingman", "Barstow", "San Bernandino", "LA"); my %cities = ( "Amarillo" => { state => "TX" }, "Barstow" => { state => "CA" }, "Chicago" => { state => "IL" }, "Flagstaff" => { state => "AZ" }, "Gallup" => { state => "TX" }, "Joplin" => { state => "MO" }, "Kingman" => { state => "AZ" }, "LA" => { state => "CA" }, "OKC" => { state => "OK" }, "Saint Looey" => { state => "MO" }, "San Bernandino" => { state => "CA" }, "Winona" => { state => "AZ" }, ); for my $i (0..$#trip-1) { my ($from,$to) = @trip[$i,$i+1]; print "$from, $cities{$from}{state} to $to, $cities{$to}{state}", $cities{$from}{state} ne $cities{$to}{state} ? " - Mann Act!" : (), "\n"; } print "\n##### Alternative #####\n"; $cities{$_}{city} = $_ for keys %cities; $_ = $cities{$_}//die("bad city $_") for @trip; dd \%cities, \@trip; for my $i (0..$#trip-1) { my ($from,$to) = @trip[$i,$i+1]; print "$from->{city}, $from->{state} to $to->{city}, " ."$to->{state}", $from->{state} ne $to->{state} ? " - Mann Act!" : (), "\n"; }

Output:

Chicago, IL to Saint Looey, MO - Mann Act! Saint Looey, MO to Joplin, MO Joplin, MO to OKC, OK - Mann Act! OKC, OK to Amarillo, TX - Mann Act! Amarillo, TX to Gallup, TX Gallup, TX to Flagstaff, AZ - Mann Act! Flagstaff, AZ to Winona, AZ Winona, AZ to Kingman, AZ Kingman, AZ to Barstow, CA - Mann Act! Barstow, CA to San Bernandino, CA San Bernandino, CA to LA, CA ##### Alternative ##### do { my $a = { "Amarillo" => { city => "Amarillo", state => "TX" }, "Barstow" => { city => "Barstow", state => "CA" }, "Chicago" => { city => "Chicago", state => "IL" }, "Flagstaff" => { city => "Flagstaff", state => "AZ" }, "Gallup" => { city => "Gallup", state => "TX" }, "Joplin" => { city => "Joplin", state => "MO" }, "Kingman" => { city => "Kingman", state => "AZ" }, "LA" => { city => "LA", state => "CA" }, "OKC" => { city => "OKC", state => "OK" }, "Saint Looey" => { city => "Saint Looey", state => "MO" }, "San Bernandino" => { city => "San Bernandino", state => "CA" }, "Winona" => { city => "Winona", state => "AZ" }, }; ( $a, [ $a->{"Chicago"}, $a->{"Saint Looey"}, $a->{"Joplin"}, $a->{"OKC"}, $a->{"Amarillo"}, $a->{"Gallup"}, $a->{"Flagstaff"}, $a->{"Winona"}, $a->{"Kingman"}, $a->{"Barstow"}, $a->{"San Bernandino"}, $a->{"LA"}, ], ); } Chicago, IL to Saint Looey, MO - Mann Act! Saint Looey, MO to Joplin, MO Joplin, MO to OKC, OK - Mann Act! OKC, OK to Amarillo, TX - Mann Act! Amarillo, TX to Gallup, TX Gallup, TX to Flagstaff, AZ - Mann Act! Flagstaff, AZ to Winona, AZ Winona, AZ to Kingman, AZ Kingman, AZ to Barstow, CA - Mann Act! Barstow, CA to San Bernandino, CA San Bernandino, CA to LA, CA

Anyway, to answer your original question, the idiom I would have used is map {@arr[$_,$_+1]} 0..$#arr-1. Note that using map purely for its side-effects is considered bad style by some, and usually a for statement modifier is also a bit easier to read. <update> To clarify, I mean that using map purely for its side effects as you've done in your original code - what I'm doing here does not fall into that category (see also my reply lower down in the thread). </update> The reason your creation of the %info hash isn't working in the original code is that you're not dereferencing the pairs.

use warnings; use strict; use Data::Dump; use List::Util 'pairs'; use List::MoreUtils 'mesh'; my @trip = ("Chicago", "Saint Looey", "Joplin", "OKC", "Amarillo", "Gallup", "Flagstaff", "Winona", "Kingman", "Barstow", "San Bernandino", "LA"); my @states = ("IL", "MO", "MO", "OK", "TX", "TX", "AZ", "AZ", "AZ", "CA", "CA", "CA" ); my @legs = map {@trip[$_,$_+1]} 0..$#trip-1; printf "%15s to %-15s\n", $_->[0], $_->[1] for pairs @legs; my %info; $info{$_->[0]}{state} = $_->[1] for pairs mesh @trip, @states; dd \%info; __END__ Chicago to Saint Looey Saint Looey to Joplin Joplin to OKC OKC to Amarillo Amarillo to Gallup Gallup to Flagstaff Flagstaff to Winona Winona to Kingman Kingman to Barstow Barstow to San Bernandino San Bernandino to LA { "Amarillo" => { state => "TX" }, "Barstow" => { state => "CA" }, "Chicago" => { state => "IL" }, "Flagstaff" => { state => "AZ" }, "Gallup" => { state => "TX" }, "Joplin" => { state => "MO" }, "Kingman" => { state => "AZ" }, "LA" => { state => "CA" }, "OKC" => { state => "OK" }, "Saint Looey" => { state => "MO" }, "San Bernandino" => { state => "CA" }, "Winona" => { state => "AZ" }, }

Replies are listed 'Best First'.
Re^2: Fence vs. Posts
by BillKSmith (Monsignor) on Jun 08, 2018 at 14:53 UTC
    I like your suggestion of the 'for modifier'. Note however, that it requires the 'push' function to do the same thing as map.
    C:\Users\Bill\forums\monks>type rodinski.pl use strict; use warnings; use Data::Dumper; my @arry = qw(A B C); my @gaps; push @gaps, [$arry[$_], $arry[$_+1]] for 0..$#arry-1; print Dumper(\@gaps); C:\Users\Bill\forums\monks>perl rodinski.pl $VAR1 = [ [ 'A', 'B' ], [ 'B', 'C' ] ];
    Bill

      Sorry, I realize now that putting those two sentences next to each other probably made my post a bit confusing. When I said "using map purely for its side-effects", I was referring to these lines in the OP's code:

      map { printf "%15s to %-15s\n", $_->[0],$_->[1] } pairs @legs; map { if ($info{$_->[0]}{state} ne $info{$_->[1]}{state}) {say "Mann +Act!"} } pairs @legs; map { $info{$_[0]}{"state"}=$_[1] } pairs mesh @trip, @states;

      The return value of map is completely ignored, and instead actions are taken inside the code block that have side effects. These are the cases where I think a for statement modifier would be better.

      On the other hand, the code that I showed, map {@arr[$_,$_+1]} 0..$#arr-1 does not make any modifications to the @arr, and I do use the return value - so this is a case where I would perfer map over for.

Re^2: Fence vs. Posts
by rodinski (Novice) on Jun 09, 2018 at 15:04 UTC

    you wrote: "I would have used is map"

     {@arr[$_,$_+1]} 0..$#arr-1

    Thanks! this is just the sort of thing I was seeking . . . new knowledge. I was unaware of $_+1 . Also later you noted:

    "The reason your creation of the %info hash isn't working in the original code is that you're not dereferencing the pairs."

    Nice, I thought I was close to getting it to work, this did the trick.

    As the lyric goes . . . I am hip to your timely tip!! Thannk again.

    However, the while pare of the code below looks like Devilry I will have to spend more time with it to figure it out

    local $_ = join "\n", @trip; printf "%15s to %-15s\n", $1, $2 while /^(.*)\n(?=(.*))/gm;

      Hi rodinski,

      I'll try to explain that last one:

      local $_ = join "\n", @trip; printf "%15s to %-15s\n", $1, $2 while /^(.*)\n(?=(.*))/gm;

      Let's remove the while for a moment and see what happens:

      my $test = "Chicago Saint Looey Joplin OKC Amarillo Gallup Flagstaff Winona Kingman Barstow San Bernandino LA" ; if ( $test =~ /^(.*)\n(?=(.*))/gm ) { printf "%15s to %-15s\n", $1, $2 ; } __END__ Chicago to Saint Looey

      (.*) = Chicago

      \n = enter

      (?=pattern) = Look around assertion. It is used to look ahead after the enter without changing the position where the last match ended. This is done so that the last arrival location becomes the depart location in the next search. https://perldoc.perl.org/perlre.html#Extended-Patterns

      (.*) = Saint Looey

      g = is in this case used to change the position where the last match ended. If you leave it out, you get a never ending loop (it will match the first found text over and over again) edit:See https://perldoc.perl.org/perlrequick.html#More-matching

      m = Treat string as multiple lines

      Now that I have had time to look at this:

      {@arr[$_,$_+1]} 0..$#arr-1

      Pardon me restating the obvious here.

      There is no new functionality that I was unaware of. In this case $_ is an integer and you are just incrementing it. I was somehow originally thinking you where feeding the block a list of cities. This is still good stuff and I appreciate it.

      I had started reading about the routines available in List::Utils and had dealing with the list values in my head. Looping over the indices may make more seance in this case.