DnfD DnfD - 4 months ago 5x
Ruby Question

Ruby Haversine Formula: Iterating Through Two Arrays

I'm having trouble iterating through two arrays using the haversine Ruby gem.

I'll post the full gist here but give you an idea of what I'm trying to accomplish.


We have a fiber network. Just think about it as lines on a map. I have all the lat/long pairs that make up this network, roughly 24,000 rows.

We have potential customers that are in the area of the fiber network. We want to see what customers are within 1,000 feet of our network. For our potential customers, we have roughly 3,000 rows.

The Haversine formula takes four arguments: lat1, long1, lat2, long2. The first two will represent the customer, the second two will represent the fiber network.

For a hardcoded customer location, I can do this and it returns the correct value in feet. Verified via Google Earth.

File.open('distance-output.csv', 'w') do |csv_object|
lat.zip(long).each do |lat_locs,long_locs|
csv_object << Haversine.distance(28.59569, -81.21464, lat_locs.pop.to_f, long_locs.pop.to_f).to_feet
csv_object << "\n"

Now, how would I implement the same type of
setup for the customer lat/long data?

If I wrap
lat.zip(long).each do |lat_locs,long_locs|
in the customer
, it returns values but incorrect (like, really incorrect). If I wrap the customer
block in
lat.zip(long).each do |lat_locs,long_locs|
, the returned values are also incorrect.

Any input on this is greatly appreciated.


I suspect your problem is that you're popping your coordinates out of the one-element arrays. That permanently modifies each array. When you do it in a loop, the second time through the loop the arrays will be empty.

The quick fix would be to use .last (which just returns the last element of the array rather than removing it) rather than .pop. Since there is only one column in each CSV, .first and .last are equivalent.

However, I suggest rewriting the part of your code that you didn't include in the question. The loops over the arrays read from your CSVs don't do anything useful at all; they just copy the arrays to new arrays.

Instead, strip off the one-element arrays, zip those, and then loop over the result:

fib_coords = fib_lat.map(&:last).zip(fib_long.map(&:last))
cust_coords = cust_lat.map(&:last).zip(cust_long.map(&:last))
fib_coords.each do |fib_lat, fib_long|
  cust_coords.each do |cust_lat, cust_long|
    # ...