Skip to content

Instantly share code, notes, and snippets.

@manmal
Last active September 12, 2015 19:10
Show Gist options
  • Select an option

  • Save manmal/a5d3ee15bf5ed65ddecc to your computer and use it in GitHub Desktop.

Select an option

Save manmal/a5d3ee15bf5ed65ddecc to your computer and use it in GitHub Desktop.
Functional Nerdgasm in Swift
typealias DistanceRange = (from: Double, to: Double) // meters
typealias WaterListResult = (water: Water, lodging: Lodging, distance: Double, price: Float?)
typealias LodgingAndDistance = (lodging: Lodging, distance: Double?)
extension Water {
// Returns waters that are sorted by their nearest (nearest to `center`) lodging's distance to `center`.
static func watersByProximity(distanceRange: DistanceRange, center: CLLocation) -> [WaterListResult] {
return ModelsStorage.waters
.flatMap { (water: Water) -> [WaterListResult] in // Map to water's lodgings combined with their distances to center
if let lodgings = water.lodgings {
let lodgingAndDistance = lodgings
.map { return LodgingAndDistance($0, $0.coordinates?.distanceTo(center)) } // attach distances to lodgings
.filter { return $0.1 != nil } // filter those with non-nil distance
.map { (lD: LodgingAndDistance) -> LodgingAndDistance in // force-unwrap filtered non-nil distances
return LodgingAndDistance(lD.lodging, lD.distance!)
}
.filter { (lD: LodgingAndDistance) -> Bool in // filter those that are within distanceRange
return lD.distance >= distanceRange.from && lD.distance < distanceRange.to
}
.sort({ $0.distance < $1.distance }) // sort lodgings of this water by distance
.first // select only first lodging
if let foundLodgingAndDistance = lodgingAndDistance {
return [WaterListResult(water: water, lodging: foundLodgingAndDistance.lodging, distance: foundLodgingAndDistance.distance!, price: nil)]
}
}
return [] // found no lodgings for this water
}
.sort({ $0.distance < $1.distance }) // sort ALL lodgings by their distance
}
}
@Noobish1
Copy link

if the lodgings property of the Water struct is [Lodging] instead of [Lodging]? it gets down to:

extension Water {

    // Returns waters that are sorted by their nearest (nearest to `center`) lodging's distance to `center`.
    static func watersByProximity(distanceRange: DistanceRange, center: CLLocation) -> [WaterListResult] {
        return ModelsStorage.waters
            .flatMap { water -> [WaterListResult] in // Map to water's lodgings combined with their distances to center
                    let lodgingAndDistance = water.lodgings
                        .flatMap { LodgingAndDistance(lodging: $0, distance: $0.coordinates?.distanceTo(center)) } // attach distances to lodgings and filter out nils
                        .filter { $0.distance >= distanceRange.from && $0.distance < distanceRange.to } // filter those that are within distanceRange
                        .sort { $0.distance < $1.distance } // sort lodgings of this water by distance
                        .first // select only first lodging

                    return lodgingAndDistance.map { [WaterListResult(water: water, lodging: $0.lodging, distance: $0.distance, price: nil)] } ?? []
            }
            .sort { $0.distance < $1.distance } // sort ALL lodgings by their distance
    }
}

@Noobish1
Copy link

I'm not sure why you're getting the first lodgingAndDistance then mapping into into an array of WaterListResults, I'm assuming you don't actually want that. If you don't you can get it down to:

extension Water {
    // Returns waters that are sorted by their nearest (nearest to `center`) lodging's distance to `center`.
    static func watersByProximity(distanceRange: DistanceRange, center: CLLocation) -> [WaterListResult] {
        return ModelsStorage.waters
                .flatMap { water in
                    let results = water.lodgings
                                        .flatMap { LodgingAndDistance(lodging: $0, distance: $0.coordinates?.distanceTo(center)) } // attach distances to lodgings, filtering out nils
                                        .filter { $0.distance >= distanceRange.from && $0.distance < distanceRange.to } // filter those that are within distanceRange
                                        .sort { $0.distance < $1.distance } // sort lodgings of this water by distance
                                        .first // select only first lodging
                                        .map { WaterListResult(water: water, lodging: $0.lodging, distance: $0.distance, price: nil) }

                    return results
                }
                .sort { $0.distance < $1.distance } // sort ALL WaterListResults by their distance
    }
}

Have to assign the results thing because otherwise swift thinks the expression is too complex

@Noobish1
Copy link

another option is this;

extension Water {
    // Returns waters that are sorted by their nearest (nearest to `center`) lodging's distance to `center`.
    static func watersByProximity(distanceRange: DistanceRange, center: CLLocation) -> [WaterListResult] {
        let toResults = { water in
            return water.lodgings
                .flatMap { LodgingAndDistance(lodging: $0, distance: $0.coordinates?.distanceTo(center)) } // attach distances to lodgings, filtering out nils
                .filter { $0.distance >= distanceRange.from && $0.distance < distanceRange.to } // filter those that are within distanceRange
                .sort { $0.distance < $1.distance } // sort lodgings of this water by distance
                .first // select only first lodging
                .map { WaterListResult(water: water, lodging: $0.lodging, distance: $0.distance, price: nil) }
        }

        return ModelsStorage.waters.flatMap(toResults).sort { $0.distance < $1.distance } // sort ALL WaterListResults by their distance
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment