Reputation: 507
I'm messing around with parsing JSON with SwiftyJSON on a swift playground. My code is as follows:
import UIKit
import SwiftyJSON
var partyList: [String] = []
var firstPresidentList: [String] = []
if let url = URL(string:"http://mysafeinfo.com/api/data?list=presidents&format=json") {
if let data = try? Data(contentsOf: url) {
let json = JSON(data: data)
for i in 1...43 {
let party = json[i]["pp"].stringValue
let president = json[i]["nm"].stringValue
if partyList.contains(party) {
print("\n")
} else {
partyList.append(party)
firstPresidentList.append(president)
}
}
print("All the different parties of U.S. presidents included "+partyList.joined(separator: ", ")+", in that order. The first presidents of those parties were (repectively) "+firstPresidentList.joined(separator: ", ")+".")
}
}
On the print
line, I was wondering how I could join the arrays with a comma and space like I have, but add "and" before the last one.
Thank you!
Upvotes: 13
Views: 6659
Reputation: 236315
Add a condition to check if your String
collection has less than or is equal to 2 elements, if true just return the two elements joined by " and "
otherwise drop the last element of your collection, join the elements with a separator ", "
then re add the last element with the final separator ", and "
.
You can extend BidirectionalCollection
protocol constraining its elements to the StringProtocol
:
Bidirectional collections offer traversal backward from any valid index, not including a collection’s startIndex. Bidirectional collections can therefore offer additional operations, such as a last property that provides efficient access to the last element and a reversed() method that presents the elements in reverse order.
Xcode 11.4 • Swift 5.2 or later
extension BidirectionalCollection where Element: StringProtocol {
var sentence: String {
count <= 2 ?
joined(separator: " and ") :
dropLast().joined(separator: ", ") + ", and " + last!
}
}
let elements = ["a", "b", "c"]
let sentenceFromElements = elements.sentence // "a, b, and c"
edit/update
Xcode 13+ • iOS15+
You can use the new generic structure ListFormatStyle
with the new instance methods of Sequence called formatted:
let elements = ["a", "b", "c"]
let formatted = elements.formatted() // "a, b, and c"
let names = ["Steve Jobs", "Wozniak", "Tim Cook", "Jony Ive"]
let formatted2and = names.formatted(.list(type: .and, width: .short)) // "Steve Jobs, Wozniak, Tim Cook, & Jony Ive"
let formatted2or = names.formatted(.list(type: .or, width: .short)) // "Steve Jobs, Wozniak, Tim Cook, or Jony Ive"
If you need a specific locale (fixed) like Portuguese Brasil:
let localeBR = Locale(identifier: "pt_BR")
let formatted2e = names.formatted(
.list(type: .and, width: .short)
.locale(localeBR)
) // "Steve Jobs, Wozniak, Tim Cook e Jony Ive"
let formatted2ou = names.formatted(
.list(type: .or, width: .short)
.locale(localeBR)
) // "Steve Jobs, Wozniak, Tim Cook ou Jony Ive"
Upvotes: 34
Reputation: 4819
Since iOS 13.0+ / macOS 10.15+ Apple provides the ListFormatter. See also here for details.
Arrays can be formatted as easy as:
let elements = ["a", "b", "c"]
result = ListFormatter.localizedString(byJoining: elements)
As the function name suggests, you also get the localization for free.
Upvotes: 21