2

I have the following code which returns places near the user's current location

import UIKit
import GooglePlaces
import CoreLocation

struct GlobalVariables {
    static var acceptedEstablishments = ["bakery", "bar", "cafe", "food", "meal_takeaway", "meal_delivery", "night_club", "restaurant", "school", "university"]
}

class ViewController: UIViewController, CLLocationManagerDelegate {

    var placesClient: GMSPlacesClient!
    var locationManager: CLLocationManager!

    // Add a pair of UILabels in Interface Builder, and connect the outlets to these variables.
    @IBOutlet var nameLabel: UILabel!
    @IBOutlet var addressLabel: UILabel!


    override func viewDidLoad() {
        super.viewDidLoad()
        placesClient = GMSPlacesClient.shared()

        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == .authorizedWhenInUse {
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.startUpdatingLocation()
        }
    }

    // Add a UIButton in Interface Builder, and connect the action to this function.
    @IBAction func getCurrentPlace(_ sender: UIButton) {

        placesClient.currentPlace(callback: { (placeLikelihoodList, error) -> Void in
            if let error = error {
                print("Pick Place error: \(error.localizedDescription)")
                return
            }

            if let placeLikelihoodList = placeLikelihoodList {
                for likelihood in placeLikelihoodList.likelihoods {
                    let place = likelihood.place
                    // only return places that are relevant to me
                    for placeType in place.types {
                        if (GlobalVariables.acceptedEstablishments.contains(placeType)) {
                            print("Current place name: \(place.name)")
                            print("Place type: \(placeType)")
                        }
                    }

                }
            }
        })
    }
}

place.types in my callback function at the bottom returns an array of strings for each place instance, which looks something like this:

["health", "point_of_interest", "establishment"]

I have a global array of strings which also contains tags such as bakery, bar, etc.

When the user presses a button, the callback function is triggered and returns places based on the nearby location.

The output looks something like this:

Current place name: LOCAL SUPERMARKET
Place type: food
Current place name: LOCAL GRILL
Place type: cafe
Current place name: LOCAL GRILL
Place type: food
Current place name: LOCAL SCHOOL
Place type: school
Current place name: LOCAL TAKEAWAY
Place type: meal_takeaway
Current place name: LOCAL TAKEAWAY
Place type: restaurant
Current place name: LOCAL TAKEAWAY
Place type: food

The same establishment is repeated multiple times because a single establishment has more than one tag associated with it.

For example:

The returned array for place.types for LOCAL TAKEAWAY is: ["meal_takeaway", "restaurant", "food"]

and because my GlobalVariables.acceptedEstablishments array contains all three of those strings, the print command will be executed three times.

How can this code be amended so that it only displays the establishment once, if the place.types array contains one or more of the matching strings? I can't seem to get my head around a solution.

nopassport1
  • 1,821
  • 1
  • 25
  • 53
  • NSSet ? NSSet where are you? Please use NSSet – Gagan_iOS Oct 10 '17 at 14:48
  • @Gagan_iOS I'm not sure what that is. Please could you provide me with an example of how to use NSSet/ what it's for? I assume it has something to do with arrays but the Array documentation on the apple website mentions nothing about it: https://developer.apple.com/documentation/swift/array Thanks in advance. I'm new to Swift but always looking to learn – nopassport1 Oct 10 '17 at 14:55
  • 1
    Look at this answer https://stackoverflow.com/questions/32439289/how-to-get-list-of-common-elements-of-2-array-in-swift better than NSSet. NSSet allows getting common objects from two collections. – Gagan_iOS Oct 10 '17 at 15:00

4 Answers4

4

The key is using Set which will not have duplicates. Set is a collection class which can be used similarly to arrays. You can iterate over it, and it has count, map, filter, contains, etc.

let acceptedPlaces: Set = ["home", "pub", "hospital"]
let availablePlaces: Set = ["home", "pub", "mountains"]
let inBoth = acceptedPlaces.intersection(availablePlaces) // ["home", "pub"]

You can easily create Sets from Arrays let someSet = Set(someArray) and the other way round let someArray = Array(someSet). You might also want to take a look at the following functions of Set: union, substract, isSuperSet, isSubset.

orkoden
  • 18,946
  • 4
  • 59
  • 50
3

You could also use sets:

if !Set(place.types).intersection(GlobalVariables.acceptedEstablishments).isEmpty
{
  // there is at least one common element
}

If you can afford to make GlobalVariables.acceptedEstablishments a Set, then the condition would be more efficient and could be written as :

if !GlobalVariables.acceptedEstablishments.intersection(places.types).isEmpty
{
  // there is at least one common element
}

In either cases places.types does not need to be a set itself.

Alain T.
  • 40,517
  • 4
  • 31
  • 51
2

The Swift Array class allows duplicate items. The Set class does not. You can create an extension on Array that has a method uniqueItems that strips away duplicates. Note that the items in the array have to be Hashable in order for this to work.

Below is the extension. (Not my code - taken from another SO post)

extension Array where Element: Hashable {
    var uniqueItems: Array  {
        var set = Set<Element>()
        return flatMap { set.insert($0).inserted ? $0 : nil }
    }
}
Duncan C
  • 128,072
  • 22
  • 173
  • 272
1

Using isDisjoint(with:):

if !Set(place.types).isDisjoint(GlobalVariables.acceptedEstablishments) {
    // There is at least one common element
}
Alexander Sandberg
  • 1,265
  • 11
  • 19