app4g
app4g

Reputation: 850

Simplify this foreach loop (to find min/max in a nested array) in swift

I would love to get rid of the foreach loop. Currently I am doing a foreach loop to populate a temp variable to separate my array to get the min/max for each Lat/Lon.

eg: slopeLatLonArray = [ [111,111],[111.1,111.2] ]

 func drawFullRouteOverlay() {
    /// Reset Array to Nil
    vLocations = []

    /// populate vLocations as a CLLocation2D
    for index in slopeLatLonArray.indices {
      vLocations.append(CLLocationCoordinate2D(latitude: Double(slopeLatLonArray[index][0]), longitude: Double(slopeLatLonArray[index][1])))
    }
    
    /// Draw the resulting polyline
    let polyline = MKPolyline(coordinates: vLocations, count: vLocations.count)
    vcTrainMapView.addOverlay(polyline)
    
    /// Bunch of stuffs to do to get the Max/Min of Lat/Lon
    var tempLat: [Double] = []
    var tempLon: [Double] = []
    
    slopeLatLonArray.forEach {
      tempLat.append($0[0])
      tempLon.append($0[1])
    }
    
    /// Zoom to the entire route polyline
    let center = CLLocationCoordinate2D(latitude : (tempLat.min()! + tempLat.max()!) / 2,
                                        longitude: (tempLon.min()! + tempLon.max()!) / 2)
    let span = MKCoordinateSpan(latitudeDelta : (tempLat.max()! - tempLat.min()!) * 1.3,
                                longitudeDelta: (tempLon.max()! - tempLon.min()!) * 1.3)
    let region = MKCoordinateRegion(center: center, span: span)
    vcTrainMapView.setRegion(region, animated: true)
  }

Upvotes: 1

Views: 429

Answers (2)

Josh
Josh

Reputation: 1718

How about either .map...

var tempLat = slopeLatLonArray.map { $0[0] }
var tempLon = slopeLatLonArray.map { $0[1] }

// Could also zip to vLocations for a 1 liner
var vLocations = zip(tempLat, tempLon).map(CLLocationCoordinate2D.init)

or setting in the initial for loop...

var tempLat: [Double] = []
var tempLon: [Double] = []
for index in slopeLatLonArray.indices {
    tempLat[index] = Double(slopeLatLonArray[index][0])
    tempLon[index] = Double(slopeLatLonArray[index][1])
    vLocations.append(CLLocationCoordinate2D(latitude: tempLat[index], longitude: tempLon[index]))
}

Upvotes: 1

Leo Dabus
Leo Dabus

Reputation: 236420

You are unnecessarily iterating all your locations multiple times. First when populating vLocations. Second when populating slopeLatLonArray. Third, fourth, fifth and sixth when getting tempLat and tempLon minimum and maximum values. And another 4 times when getting them again for the span (this might be optimized by the compiler but I am not sure).

What I suggest is to get all those values during the first iteration when populating vLocations. This way you will iterate all locations only once:

func drawFullRouteOverlay() {
    guard let first = slopeLatLonArray.first, first.count == 2 else { return }
    var minLatitude = first[0]
    var maxLatitude = first[0]
    var minLongitude = first[1]
    var maxLongitude = first[1]
    vLocations = slopeLatLonArray.map {
        let latitude = $0[0]
        let longitude = $0[1]
        minLatitude = min(minLatitude, latitude)
        maxLatitude = max(maxLatitude, latitude)
        minLongitude = min(minLongitude, longitude)
        maxLongitude = max(maxLongitude, longitude)
        return .init(latitude: latitude, longitude: longitude)
    }
    /// Draw the resulting polyline
    let polyline = MKPolyline(coordinates: vLocations, count: vLocations.count)
    vcTrainMapView.addOverlay(polyline)
    
    /// Zoom to the entire route polyline
    let center = CLLocationCoordinate2D(latitude: (minLatitude + maxLatitude) / 2, longitude: (minLongitude + maxLongitude) / 2)
    let span = MKCoordinateSpan(latitudeDelta: (maxLatitude - minLatitude) * 1.3, longitudeDelta: (maxLongitude - minLongitude) * 1.3)
    let region = MKCoordinateRegion(center: center, span: span)
    vcTrainMapView.setRegion(region, animated: true)
}

Upvotes: 1

Related Questions