14

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!

2 Answers 2

35

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"
Sign up to request clarification or add additional context in comments.

Comments

21

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.