-1

I have an array called carArray which is an array of Cars.
How can I write a function to determine if the array contains a specific model? For example if a user inputs "BMW", how can I determine if that is in the array? I am trying to avoid answering this problem with writing a for loop to loop through the entire array each time this happens.

struct Car {
    var make: String?
    var model: String?
    var year: Double?
}

var carArray: [Car] = []

carArray.append(Car(make: "Audi", model: "S5", year: 2015))
carArray.append(Car(make: "BMW", model: "X3", year: 2016))
carArray.append(Car(make: "Honda", model: "Accord", year: 2018))
Josh
  • 23
  • 4
  • What is wrong with writing a loop? – Scott Hunter Jul 19 '21 at 19:25
  • Just because you use a for loop doesn’t mean you have to iterate over the whole array, you can use `break` or `return` when you have a match. You should really include your own attempt to solve your problem when posting a question like this. – Joakim Danielson Jul 19 '21 at 19:49
  • Why are all struct members optional (and mutable)? This makes the comparison more difficult. – vadian Jul 19 '21 at 19:53

2 Answers2

1

Add something like this

func contains(make: String) -> Bool {
    carArray.compactMap(\.make).contains(make)
} // returns true in your case for "BMW"

This has two parts. First, I am mapping the array of Car objects to an array of String? by mapping over the make key path. Since this may have nil values, I am using compactMap to remove the nil values.

If you are going to be doing this a lot and don't want the overhead of mapping and checking every time then create something to store your cars. Something like this:

struct Car {
    var make: String?
    var model: String?
    var year: Double?
}

struct CarStore {
    private(set) var  storage: [Car] = .init()
    private var makes: Set<String> = .init()

    mutating func add(car: Car) {
        storage.append(car)
        car.make.map { makes.insert($0) } // map to run the closure only if make is not nil
    }

    func contains(make: String) -> Bool {
        makes.contains(make)
    }
}

var store = CarStore()

store.add(car: Car(make: "Audi", model: "S5", year: 2015))
store.add(car: Car(make: "BMW", model: "X3", year: 2016))
store.add(car: Car(make: "Honda", model: "Accord", year: 2018))

store.contains(make: "BMW") // -> true
Abizern
  • 146,289
  • 39
  • 203
  • 257
  • Why not use `contains(where:)` as in Trevor's answer? – Duncan C Jul 19 '21 at 19:54
  • @DuncanC because that's the way I wrote it first, and I left it as an example of mapping over key paths. – Abizern Jul 19 '21 at 20:18
  • Thanks for the answer. In your first function `contains(make: String)` can I also have the function, if true, return the location in the array that made it true? Thanks – Josh Jul 19 '21 at 22:49
  • @Josh Not in my example as it stands because it uses a Set. In your original code you can use `carArray.firstIndex { $0.make == "BMW" }`. That should be enough of a hint to move you forward. – Abizern Jul 19 '21 at 22:53
1

You can use the contains(where:) function to do this:

carArray.contains(where: { $0.make == "BMW" })

https://developer.apple.com/documentation/swift/array/2297359-contains

edit: sorry missed your other part. If you want to avoid looping through the array each time then you'd have to store an additional data structure like a Set of each make.

telkins
  • 10,440
  • 8
  • 52
  • 79