i have one arrays for two defrent city to choose from the goal is to compare these two cities and the result will be the distince between them i acheved that ,but the problem is it takes so muches line of code i think its pssible to be less than that ,
what i did so far :
the result distince is define as below :
let from_A_to_b = 423
let from_A_to_c = 1439
let from_A_to_d = 1122
...
and i have one array prisnted in tableview to choose from and pass the data:
var city = ["A","B","C","D"]
the compare code is :
if (passedCity.text == "A") && (passedCity2.text == "B") {
result.text = "\(from_A_to_b) km "
} else if (passedCity.text == "A") && (passedCity2.text == "C") {
result.text = "\(from_A_to_c) km "
} else if (passedCity.text == "A") && (passedCity2.text == "D") {
result.text = "\(from_A_to_d) km "
}
in this case i will repeat the code multible times , i think i could make this work with for in loop but i dont know how sorry for this beginner Q
CodePudding user response:
As long as your data table is hardcoded where each data has its worn property there is no non-hacky way to accomplish this with less code then one line per entry.
(A "hacky" way would be to generate a string representing a name of property and constructing a selector from string to access property directly, unsafely...)
In any of cases I would first create an enumeration with your cities
enum City: CaseIterable {
case cityA
case cityB
case cityC
case cityD
}
then use some display name such as
extension City {
var displayName: String {
switch self {
case .cityA: return "City A"
case .cityB: return "City B"
case .cityC: return "City C"
case .cityD: return "City D"
}
}
}
I would try to have 2 properties in your code as
var selectedCityFrom: City
var selectedCityTo: City
and use those directly instead of converting them to-from strings. But if you insist on strings you can now do:
extension City {
static func fromName(_ displayName: String) -> City? {
self.allCases.first(where: { $0.displayName == displayName })
}
Doing this you now solve getting a concrete object from your strings and you can do:
guard let fromText = passedCity.text, let fromCity = City.fromName(fromText) else { return }
guard let toText = passedCity2.text, let toCity = City.fromName(toText) else { return }
let distance = City.getDistanceBetween(fromCity, toCity)
print(distance)
And now on the other side it is probably easiest to just have an array of your data such as [(from: City, to: City, distance: Double)]
:
extension City {
private static let distances: [(from: City, to: City, distance: Double)] = [
(.cityA, .cityB, 423),
(.cityA, .cityC, 1439),
(.cityA, .cityD, 1122)
]
static func getDistanceBetween(_ from: City, _ to: City) -> Double? {
guard from != to else { return 0.0 } // It is a same city
guard let match = City.distances.first(where: { ($0.from == from && $0.to == to) || ($0.to == from && $0.from == to) }) else { return nil } // Data for this combination is missing
return match.distance
}
}
Probably it makes more sense that you actually move this table to some external JSON which can be bundled with application or even sent via some API from remote server. In that case it is more likely you will rather generate keys like cityA_cityB
and have a table [String: Double]
. For instance:
{
"cityA_cityB": 423,
"cityA_cityC": 1439,
"cityA_cityD": 1122
}
then you would need to build something like this:
extension City {
var JSONKey: String {
switch self {
case .cityA: return "cityA"
case .cityB: return "cityB"
case .cityC: return "cityC"
case .cityD: return "cityD"
}
}
static func getDistanceBetween(_ from: City, _ to: City, fromJSON jsonData: [String: Double]) -> Double? {
guard from != to else { return 0.0 } // It is a same city
let normalKey = [from.JSONKey, to.JSONKey].joined(separator: "_")
let reversedKey = [to.JSONKey, from.JSONKey].joined(separator: "_")
return jsonData[normalKey] ?? jsonData[reversedKey]
}
But there are very many ways to accomplish this same result. I hope this helps you finding the one that suits you best.