Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Nested foreach loops

by keienn (Novice)
on Sep 07, 2016 at 21:28 UTC ( #1171336=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks, I have a csv file with six columns as below;

NameA Longitude Latitude NameB Longitude Latitude
10001_NI0001, 36.79887354, -1.26122956, " WL3762", 34.52328889 , -1.007941667
NI0006, 36.86998613, -1.295393144, " NM5286", 36.83137418, -1.172626372
NI0066, 36.82748524, -1.25734101, " EC4140", 37.4580536, -0.53351668,

Am calling a subroutine 'distance' to calculate the distance between NameA and NameB. Here's where am stuck, how do I loop the first item on NameA column to iterate over all NameBs and return the smallest distance, together with both corresponding NameA and NameB entries then move to the next row and repeat above, and so on till end of file. I imagine a hash of arrays with NameA as keys will be appropriate to hold that result, but first i need to get over the loop part which i think is a nested for or foreach loop or such. Being new to Perl, I have not used the CSV module, wanted to try split first for better understanding. Below is a part of the relevant code and a sample of the output am getting;

open COMBINED, "<$outf" or die "couldn't open $outf\n"; while (<COMBINED>) { chomp $_; no warnings 'uninitialized'; @coords = (split /,/,$_); my $lat2G = $coords[2]; my $lon2G = $coords[1]; my $lat3G = $coords[5]; my $lon3G = $coords[4]; my $gsm_site = $coords[0]; my $umts_site = $coords[3]; #} my @coords2G = ($lat2G, $lon2G,); my @coords3G = ($lat3G, $lon3G,); foreach my $coord2G(@coords2G) { print $gsm_site,$umts_site,' ', distance(@coords2G, @coord +s3G, "K")."\n"; } }

The comma's are manually inserted for readability, not part of the output (which is tab I think)
NameA, NameB, 0
NameA, NameB, 0
10001_NI0001, WL3762, 254.534319729278
10001_NI0001, WL3762, 254.534319729278
NI0006, NM5286, 14.309312591142
NI0006, NM5286, 14.3093125911424

Replies are listed 'Best First'.
Re: Nested foreach loops
by NetWallah (Canon) on Sep 08, 2016 at 02:42 UTC
    Perhaps something like this (You need to provide the 'distance' sub):
    use strict; use warnings; my (@gsm_site, @umts_site); while (<DATA>) { my ($name1,$lon1,$lat1, $name2,$lon2,$lat2) = split /[\s"]*,["\s]*/ +, $_; next unless $lat1 and $lon1; # Avoid title lines push @gsm_site, {NAME=>$name1, LAT=>$lat1, LON=>$lon1}; push @umts_site,{NAME=>$name2, LAT=>$lat2, LON=>$lon2}; } for my $g (@gsm_site){ $g->{NEAREST} = $umts_site[0]; # Initial assumption til +l we know better $g->{DIST_TO_NEAREST} = distance($g->{LAT},$g->{LON}, $g->{NEARE +ST}{LAT},$g->{NEAREST}{LON}, "K"); for my $u (@umts_site[1..$#umts_site]){ my $this_distance = distance($g->{LAT},$g->{LON}, $u->{LAT},$ +u->{LON}, "K"); next unless $this_distance < $g->{DIST_TO_NEAREST}; $g->{NEAREST} = $u; $g->{DIST_TO_NEAREST} = $this_distance; } } # Print them out for my $g (@gsm_site){ print "$g->{NAME}, $g->{NEAREST}->{NAME}, $g->{DIST_TO_NEAREST},\n" +; } __DATA__ NameA Longitude Latitude NameB Longitude Latitude 10001_NI0001, 36.79887354, -1.26122956, " WL3762", 34.52328889 , -1.00 +7941667 NI0006, 36.86998613, -1.295393144, " NM5286", 36.83137418, -1.17262637 +2 NI0066, 36.82748524, -1.25734101, " EC4140", 37.4580536, -0.53351668,
    Note - you should really use a module like Text::CSV, instead of the split hack, above.

            ...it is unhealthy to remain near things that are in the process of blowing up.     man page for WARP, by Larry Wall

      Thank you Monks, for your great help. I did try NetWallah's code,thanks O great Abbot. I am however getting some errors originating from the subroutine which am sharing here as you had suggested. I previously got such errors while trying different loops but avoided tinkering with the subroutine which works as it is. What should i change in the new code? Here are the exceptions am getting and the subroutine code;

      The errors, the line 5852 is the last line in my CSV file
      C:\MyScripts\2G3Gdistance.pl
      Argument "Longitude" isn't numeric in subtraction (-) at
      C:\MyScripts\2G3Gdistance.pl line 53, <COMBINED> line 5852 (#1)
      Argument "Latitude" isn't numeric in multiplication (*) at
      C:\MyScripts\2G3Gdistance.pl line 80, <COMBINED> line 5852 (#1)
      Use of uninitialized value $lon2 in subtraction (-) at
      C:\MyScripts\2G3Gdistance.pl line 53, <COMBINED> line 5852 (#2)
      (W uninitialized) An undefined value was used as if it were already
      defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
      To suppress this warning assign a defined value to your variables.
      Use of uninitialized value $deg in multiplication (*) at
      C:\MyScripts\2G3Gdistance.pl line 80, <COMBINED> line 5852 (#2)
      Uncaught exception from user code:
      Can't take sqrt of -4.44089e-016 at C:\MyScripts\2G3Gdistance.pl line 71, <COMBINED> line 5852.
      main::acos(1) called at C:\MyScripts\2G3Gdistance.pl line 55
      main::distance(-0.433525648, 36.968599085, -0.433525648, 36.96859909, "K") called at C:\MyVolume\Development\MyScripts\2G3Gdistance.pl line 150

      The subroutine code

      my $pi = atan2(1,1) * 4; sub distance { my ($lat1, $lon1, $lat2, $lon2, $unit) = @_; my $theta = $lon1 - $lon2; my $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad +($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $dist = $dist * 60 * 1.1515; if ($unit eq "K") { $dist = $dist * 1.609344; } elsif ($unit eq "N") { $dist = $dist * 0.8684; } return ($dist); } #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: #::: This function get the arccos function using arctan function :: +: #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: sub acos { my ($rad) = @_; my $ret = atan2(sqrt(1 - $rad**2), $rad); return $ret; } #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: #::: This function converts decimal degrees to radians :: +: #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: sub deg2rad { my ($deg) = @_; return ($deg * $pi / 180); } #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: #::: This function converts radians to decimal degrees :: +: #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: sub rad2deg { my ($rad) = @_; return ($rad * 180 / $pi); } print distance(32.9697, -96.80322, 29.46786, -98.53506, "K") . " Kilom +eters\n";
        Argument "Longitude" isn't numeric in subtraction (-) at C:\MyScripts\ +2G3Gdistance.pl line 53, <COMBINED> line 5852 (#1) Argument "Latitude" isn't numeric in multiplication (*) at C:\MyScript +s\2G3Gdistance.pl line 80, <COMBINED> line 5852 (#1)

        These are clearly the results of you trying to perform arithmetic on the values obtained from your data file's header row. Don't do that. Only start processing the actual data from row 2 onwards.

        Use of uninitialized value $lon2 in subtraction (-) at C:\MyScripts\2G3Gdistance.pl line 53, <COMBINED> line 5852 (#2)

        This suggests that you have not parsed the data file correctly because $lon2 remains undefined. I suggest that you print out the values of all the arguments to distance() as soon as you retrieve them in order to determine how the parsing has failed and then correct that. The later errors are likely propagations of this problem.

        Do take a look through the Basic debugging checklist if you have not already done so. It will help you clear up such similar problems in future.

Re: Nested foreach loops
by Anonymous Monk on Sep 07, 2016 at 22:24 UTC

    Here's where am stuck, how do I loop the first item on NameA column to iterate over all NameBs and return the smallest distance, together with both corresponding NameA and NameB entries then move to the next row and repeat above, and so on till end of file.

    What does that mean?

    ?"Loop first item"? What distance?

    If you could please use the numbers from the three lines of data you posted, and in pencil-and-paper fashion , solve the problem step by step for those three lines, this would help me understand what you're asking

    Its hard to interpret a program that doesn't work to understand the question being asked

    Probably need to read the file into a hash of arrays first, then query it like a database, do some calculations ...

Re: Nested foreach loops
by Cow1337killr (Monk) on Sep 09, 2016 at 23:05 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1171336]
Approved by stevieb
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (2)
As of 2022-05-18 23:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you prefer to work remotely?



    Results (71 votes). Check out past polls.

    Notices?