splitting Swift string complex












0















I would like to split a response which is in plain string. Response is as



Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead


Need to create a dictionary from this response like, ResultSetDic[String:String]



Status: N/A  
Host: somesite.com
is Connection live: true
Status Connection: deny
heart beat: dead


tried splitting the response string with various ways Like NSRegularExpression, Range, Split but none of them is cleaner and working multiple string result sets to parse one by one which is not neat.



Any helpful way to split the response in a dictionary.
In above response keys are always fixed.










share|improve this question

























  • Where are you getting this data from? Do they have any machine parsable API available? (JSON, XML, YAML, etc.?)

    – Alexander
    Aug 24 '18 at 16:56











  • Nope this data is send by a server but in an string format. (Not JSON, i wish it was)

    – iamMobile
    Aug 24 '18 at 17:06
















0















I would like to split a response which is in plain string. Response is as



Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead


Need to create a dictionary from this response like, ResultSetDic[String:String]



Status: N/A  
Host: somesite.com
is Connection live: true
Status Connection: deny
heart beat: dead


tried splitting the response string with various ways Like NSRegularExpression, Range, Split but none of them is cleaner and working multiple string result sets to parse one by one which is not neat.



Any helpful way to split the response in a dictionary.
In above response keys are always fixed.










share|improve this question

























  • Where are you getting this data from? Do they have any machine parsable API available? (JSON, XML, YAML, etc.?)

    – Alexander
    Aug 24 '18 at 16:56











  • Nope this data is send by a server but in an string format. (Not JSON, i wish it was)

    – iamMobile
    Aug 24 '18 at 17:06














0












0








0


0






I would like to split a response which is in plain string. Response is as



Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead


Need to create a dictionary from this response like, ResultSetDic[String:String]



Status: N/A  
Host: somesite.com
is Connection live: true
Status Connection: deny
heart beat: dead


tried splitting the response string with various ways Like NSRegularExpression, Range, Split but none of them is cleaner and working multiple string result sets to parse one by one which is not neat.



Any helpful way to split the response in a dictionary.
In above response keys are always fixed.










share|improve this question
















I would like to split a response which is in plain string. Response is as



Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead


Need to create a dictionary from this response like, ResultSetDic[String:String]



Status: N/A  
Host: somesite.com
is Connection live: true
Status Connection: deny
heart beat: dead


tried splitting the response string with various ways Like NSRegularExpression, Range, Split but none of them is cleaner and working multiple string result sets to parse one by one which is not neat.



Any helpful way to split the response in a dictionary.
In above response keys are always fixed.







ios swift






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 19 '18 at 7:37









Cœur

17.8k9106145




17.8k9106145










asked Aug 24 '18 at 16:52









iamMobileiamMobile

54821230




54821230













  • Where are you getting this data from? Do they have any machine parsable API available? (JSON, XML, YAML, etc.?)

    – Alexander
    Aug 24 '18 at 16:56











  • Nope this data is send by a server but in an string format. (Not JSON, i wish it was)

    – iamMobile
    Aug 24 '18 at 17:06



















  • Where are you getting this data from? Do they have any machine parsable API available? (JSON, XML, YAML, etc.?)

    – Alexander
    Aug 24 '18 at 16:56











  • Nope this data is send by a server but in an string format. (Not JSON, i wish it was)

    – iamMobile
    Aug 24 '18 at 17:06

















Where are you getting this data from? Do they have any machine parsable API available? (JSON, XML, YAML, etc.?)

– Alexander
Aug 24 '18 at 16:56





Where are you getting this data from? Do they have any machine parsable API available? (JSON, XML, YAML, etc.?)

– Alexander
Aug 24 '18 at 16:56













Nope this data is send by a server but in an string format. (Not JSON, i wish it was)

– iamMobile
Aug 24 '18 at 17:06





Nope this data is send by a server but in an string format. (Not JSON, i wish it was)

– iamMobile
Aug 24 '18 at 17:06












3 Answers
3






active

oldest

votes


















1














Since you have a fixed format you're parsing, with known keys, and the same format every time, this is a ready made problem for Regular Expressions:



let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

extension String {
subscript(range:NSRange) -> Substring {
let start = index(startIndex, offsetBy: range.location)
let end = index(start, offsetBy: range.length)

return self[start..<end]
}
}

func parse(response:String) -> [String:String]? {
guard let regex = try? NSRegularExpression(pattern: "Result Set, Status: (.*) Host: (.*) is Connection live: (.*) Status Connection: (.*) heart beat: (.*)", options:) else {
return nil
}

guard let match = regex.firstMatch(in: input, range:NSRange(input.startIndex..<input.endIndex, in: input)) else {
return nil
}

// Note that a much better approach here would be to *not* return
// a dictionary, but instead to return a struct containing all
// of the relevant data
return [
"Status": String(input[match.range(at: 1)]),
"Host": String(input[match.range(at:2)]),
"is Connection live": String(input[match.range(at:3)]),
"Status Connection": String(input[match.range(at: 4)]),
"heart beat": String(input[match.range(at:5)])
]
}





share|improve this answer































    2














    The thing what makes this parsable is, that each value is only a single word and that key and value are separated by :.



    First I identify the the end of the next key, then add the next word as its value.



    func testing() {
    let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

    var step1 = input.split(separator: " ")
    guard let index = step1.firstIndex(where: { $0.contains(",") }) else {
    fatalError("Does not contain `,`. at the beginning.")
    }
    step1.removeFirst(index + 1)

    var step2 = step1
    var output = [String: String]()
    repeat {
    guard let index = step2.firstIndex(where: { $0.contains(":") }) else {
    // If the last part has no :, can add it under `end`.
    // output["end"] = step2.joined(separator: " ")
    step2.removeAll()
    break
    }
    let key = step2[0...index].joined(separator: " ").trimmingCharacters(in: CharacterSet(charactersIn: ":"))
    let value = step2[index + 1]
    output [key] = String(value)
    step2.removeFirst(index + 2)

    } while step2.count != 0

    output.forEach{
    print("($0.key): ($0.value)")
    }
    }


    // Output:
    // Host: somesite.com
    // Status Connection: deny
    // Status: N/A
    // heart beat: dead
    // is Connection live: true


    Edit: Array.firstIndex(where:) seems to be Xcode10+, so in Xcode9.4 you may try:



    extension Array {
    func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Int? {
    for index in indices where try predicate(self[index]) { return index }
    return nil
    }
    }


    Edit2: A backward-compatible way to add .firstIndex(where:) from Leo:



    extension Collection {
    func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
    return try index(where: predicate)
    }
    }





    share|improve this answer


























    • Thanks Purpose. I'm unable to see step1.firstIndex. Gettting error in Playground string-operations.playground:161:27: error: value of type '[String.SubSequence]' (aka 'Array<Substring>') has no member 'firstIndex' guard let index = step1.firstIndex(where: { $0.contains(":") }) else {

      – iamMobile
      Aug 24 '18 at 17:16













    • change it index(of:) and index(where:)

      – Leo Dabus
      Aug 24 '18 at 17:27













    • @iamMobile The function seems to have been added in Xcode10/Swift4.2, try the extension above for an alternative. Or try Leo's tip. I can't find them in the Array docs but they may have removed deprecated/renamed functions from it.

      – Fabian
      Aug 24 '18 at 17:30








    • 1





      @Purpose better to use the array indices property to iterate through it. for index in indices where try predicate(self[index]) { return index }

      – Leo Dabus
      Aug 24 '18 at 17:52








    • 1





      @LeoDabus Damn this makes things short, very nice!

      – Fabian
      Aug 24 '18 at 17:55



















    1














    Since you have fixed keys and response format, it might be better go with specific parsing logics with your keys.



    let response = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

    enum ResponseParseId: String {
    case status = "Status:"
    case host = "Host:"
    case isConnectionLive = "is Connection live:"
    case statusConnection = "Status Connection:"
    case heartBeat = "heart beat:"

    var key: String {
    switch self {
    case .status:
    return "Status"
    case .host:
    return "Host"
    case .isConnectionLive:
    return "is Connection live"
    case .statusConnection:
    return "Status Connection"
    case .heartBeat:
    return "heart beat"
    }
    }

    static var allIds: [ResponseParseId] = [.status, .host, .isConnectionLive, .statusConnection, .heartBeat]
    }

    func getValue(from response: String, for key: ResponseParseId) -> String? {
    let components = response.components(separatedBy: key.rawValue)
    if let substring = components.last?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) {
    let subcomponents = substring.components(separatedBy: " ")
    if let value = subcomponents.first {
    return value
    }
    }
    return nil
    }

    func getResponseDictionary(response: String) -> [String: String] {
    var dictionary: [String: String] = [:]
    for id in ResponseParseId.allIds {
    if let value = getValue(from: response, for: id) {
    dictionary[id.key] = value
    }
    }
    return dictionary
    }

    let dictionary = getResponseDictionary(response: response)

    print(dictionary)





    share|improve this answer
























    • Interesting approach! Since unknown keys can't be used anyway it makes sense to define them, makes things more sturdy and easier to use with -> [ResponseParseID: String].

      – Fabian
      Aug 24 '18 at 21:49











    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52008779%2fsplitting-swift-string-complex%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    Since you have a fixed format you're parsing, with known keys, and the same format every time, this is a ready made problem for Regular Expressions:



    let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

    extension String {
    subscript(range:NSRange) -> Substring {
    let start = index(startIndex, offsetBy: range.location)
    let end = index(start, offsetBy: range.length)

    return self[start..<end]
    }
    }

    func parse(response:String) -> [String:String]? {
    guard let regex = try? NSRegularExpression(pattern: "Result Set, Status: (.*) Host: (.*) is Connection live: (.*) Status Connection: (.*) heart beat: (.*)", options:) else {
    return nil
    }

    guard let match = regex.firstMatch(in: input, range:NSRange(input.startIndex..<input.endIndex, in: input)) else {
    return nil
    }

    // Note that a much better approach here would be to *not* return
    // a dictionary, but instead to return a struct containing all
    // of the relevant data
    return [
    "Status": String(input[match.range(at: 1)]),
    "Host": String(input[match.range(at:2)]),
    "is Connection live": String(input[match.range(at:3)]),
    "Status Connection": String(input[match.range(at: 4)]),
    "heart beat": String(input[match.range(at:5)])
    ]
    }





    share|improve this answer




























      1














      Since you have a fixed format you're parsing, with known keys, and the same format every time, this is a ready made problem for Regular Expressions:



      let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

      extension String {
      subscript(range:NSRange) -> Substring {
      let start = index(startIndex, offsetBy: range.location)
      let end = index(start, offsetBy: range.length)

      return self[start..<end]
      }
      }

      func parse(response:String) -> [String:String]? {
      guard let regex = try? NSRegularExpression(pattern: "Result Set, Status: (.*) Host: (.*) is Connection live: (.*) Status Connection: (.*) heart beat: (.*)", options:) else {
      return nil
      }

      guard let match = regex.firstMatch(in: input, range:NSRange(input.startIndex..<input.endIndex, in: input)) else {
      return nil
      }

      // Note that a much better approach here would be to *not* return
      // a dictionary, but instead to return a struct containing all
      // of the relevant data
      return [
      "Status": String(input[match.range(at: 1)]),
      "Host": String(input[match.range(at:2)]),
      "is Connection live": String(input[match.range(at:3)]),
      "Status Connection": String(input[match.range(at: 4)]),
      "heart beat": String(input[match.range(at:5)])
      ]
      }





      share|improve this answer


























        1












        1








        1







        Since you have a fixed format you're parsing, with known keys, and the same format every time, this is a ready made problem for Regular Expressions:



        let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

        extension String {
        subscript(range:NSRange) -> Substring {
        let start = index(startIndex, offsetBy: range.location)
        let end = index(start, offsetBy: range.length)

        return self[start..<end]
        }
        }

        func parse(response:String) -> [String:String]? {
        guard let regex = try? NSRegularExpression(pattern: "Result Set, Status: (.*) Host: (.*) is Connection live: (.*) Status Connection: (.*) heart beat: (.*)", options:) else {
        return nil
        }

        guard let match = regex.firstMatch(in: input, range:NSRange(input.startIndex..<input.endIndex, in: input)) else {
        return nil
        }

        // Note that a much better approach here would be to *not* return
        // a dictionary, but instead to return a struct containing all
        // of the relevant data
        return [
        "Status": String(input[match.range(at: 1)]),
        "Host": String(input[match.range(at:2)]),
        "is Connection live": String(input[match.range(at:3)]),
        "Status Connection": String(input[match.range(at: 4)]),
        "heart beat": String(input[match.range(at:5)])
        ]
        }





        share|improve this answer













        Since you have a fixed format you're parsing, with known keys, and the same format every time, this is a ready made problem for Regular Expressions:



        let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

        extension String {
        subscript(range:NSRange) -> Substring {
        let start = index(startIndex, offsetBy: range.location)
        let end = index(start, offsetBy: range.length)

        return self[start..<end]
        }
        }

        func parse(response:String) -> [String:String]? {
        guard let regex = try? NSRegularExpression(pattern: "Result Set, Status: (.*) Host: (.*) is Connection live: (.*) Status Connection: (.*) heart beat: (.*)", options:) else {
        return nil
        }

        guard let match = regex.firstMatch(in: input, range:NSRange(input.startIndex..<input.endIndex, in: input)) else {
        return nil
        }

        // Note that a much better approach here would be to *not* return
        // a dictionary, but instead to return a struct containing all
        // of the relevant data
        return [
        "Status": String(input[match.range(at: 1)]),
        "Host": String(input[match.range(at:2)]),
        "is Connection live": String(input[match.range(at:3)]),
        "Status Connection": String(input[match.range(at: 4)]),
        "heart beat": String(input[match.range(at:5)])
        ]
        }






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Aug 24 '18 at 23:33









        David BerryDavid Berry

        31.6k106981




        31.6k106981

























            2














            The thing what makes this parsable is, that each value is only a single word and that key and value are separated by :.



            First I identify the the end of the next key, then add the next word as its value.



            func testing() {
            let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

            var step1 = input.split(separator: " ")
            guard let index = step1.firstIndex(where: { $0.contains(",") }) else {
            fatalError("Does not contain `,`. at the beginning.")
            }
            step1.removeFirst(index + 1)

            var step2 = step1
            var output = [String: String]()
            repeat {
            guard let index = step2.firstIndex(where: { $0.contains(":") }) else {
            // If the last part has no :, can add it under `end`.
            // output["end"] = step2.joined(separator: " ")
            step2.removeAll()
            break
            }
            let key = step2[0...index].joined(separator: " ").trimmingCharacters(in: CharacterSet(charactersIn: ":"))
            let value = step2[index + 1]
            output [key] = String(value)
            step2.removeFirst(index + 2)

            } while step2.count != 0

            output.forEach{
            print("($0.key): ($0.value)")
            }
            }


            // Output:
            // Host: somesite.com
            // Status Connection: deny
            // Status: N/A
            // heart beat: dead
            // is Connection live: true


            Edit: Array.firstIndex(where:) seems to be Xcode10+, so in Xcode9.4 you may try:



            extension Array {
            func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Int? {
            for index in indices where try predicate(self[index]) { return index }
            return nil
            }
            }


            Edit2: A backward-compatible way to add .firstIndex(where:) from Leo:



            extension Collection {
            func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
            return try index(where: predicate)
            }
            }





            share|improve this answer


























            • Thanks Purpose. I'm unable to see step1.firstIndex. Gettting error in Playground string-operations.playground:161:27: error: value of type '[String.SubSequence]' (aka 'Array<Substring>') has no member 'firstIndex' guard let index = step1.firstIndex(where: { $0.contains(":") }) else {

              – iamMobile
              Aug 24 '18 at 17:16













            • change it index(of:) and index(where:)

              – Leo Dabus
              Aug 24 '18 at 17:27













            • @iamMobile The function seems to have been added in Xcode10/Swift4.2, try the extension above for an alternative. Or try Leo's tip. I can't find them in the Array docs but they may have removed deprecated/renamed functions from it.

              – Fabian
              Aug 24 '18 at 17:30








            • 1





              @Purpose better to use the array indices property to iterate through it. for index in indices where try predicate(self[index]) { return index }

              – Leo Dabus
              Aug 24 '18 at 17:52








            • 1





              @LeoDabus Damn this makes things short, very nice!

              – Fabian
              Aug 24 '18 at 17:55
















            2














            The thing what makes this parsable is, that each value is only a single word and that key and value are separated by :.



            First I identify the the end of the next key, then add the next word as its value.



            func testing() {
            let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

            var step1 = input.split(separator: " ")
            guard let index = step1.firstIndex(where: { $0.contains(",") }) else {
            fatalError("Does not contain `,`. at the beginning.")
            }
            step1.removeFirst(index + 1)

            var step2 = step1
            var output = [String: String]()
            repeat {
            guard let index = step2.firstIndex(where: { $0.contains(":") }) else {
            // If the last part has no :, can add it under `end`.
            // output["end"] = step2.joined(separator: " ")
            step2.removeAll()
            break
            }
            let key = step2[0...index].joined(separator: " ").trimmingCharacters(in: CharacterSet(charactersIn: ":"))
            let value = step2[index + 1]
            output [key] = String(value)
            step2.removeFirst(index + 2)

            } while step2.count != 0

            output.forEach{
            print("($0.key): ($0.value)")
            }
            }


            // Output:
            // Host: somesite.com
            // Status Connection: deny
            // Status: N/A
            // heart beat: dead
            // is Connection live: true


            Edit: Array.firstIndex(where:) seems to be Xcode10+, so in Xcode9.4 you may try:



            extension Array {
            func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Int? {
            for index in indices where try predicate(self[index]) { return index }
            return nil
            }
            }


            Edit2: A backward-compatible way to add .firstIndex(where:) from Leo:



            extension Collection {
            func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
            return try index(where: predicate)
            }
            }





            share|improve this answer


























            • Thanks Purpose. I'm unable to see step1.firstIndex. Gettting error in Playground string-operations.playground:161:27: error: value of type '[String.SubSequence]' (aka 'Array<Substring>') has no member 'firstIndex' guard let index = step1.firstIndex(where: { $0.contains(":") }) else {

              – iamMobile
              Aug 24 '18 at 17:16













            • change it index(of:) and index(where:)

              – Leo Dabus
              Aug 24 '18 at 17:27













            • @iamMobile The function seems to have been added in Xcode10/Swift4.2, try the extension above for an alternative. Or try Leo's tip. I can't find them in the Array docs but they may have removed deprecated/renamed functions from it.

              – Fabian
              Aug 24 '18 at 17:30








            • 1





              @Purpose better to use the array indices property to iterate through it. for index in indices where try predicate(self[index]) { return index }

              – Leo Dabus
              Aug 24 '18 at 17:52








            • 1





              @LeoDabus Damn this makes things short, very nice!

              – Fabian
              Aug 24 '18 at 17:55














            2












            2








            2







            The thing what makes this parsable is, that each value is only a single word and that key and value are separated by :.



            First I identify the the end of the next key, then add the next word as its value.



            func testing() {
            let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

            var step1 = input.split(separator: " ")
            guard let index = step1.firstIndex(where: { $0.contains(",") }) else {
            fatalError("Does not contain `,`. at the beginning.")
            }
            step1.removeFirst(index + 1)

            var step2 = step1
            var output = [String: String]()
            repeat {
            guard let index = step2.firstIndex(where: { $0.contains(":") }) else {
            // If the last part has no :, can add it under `end`.
            // output["end"] = step2.joined(separator: " ")
            step2.removeAll()
            break
            }
            let key = step2[0...index].joined(separator: " ").trimmingCharacters(in: CharacterSet(charactersIn: ":"))
            let value = step2[index + 1]
            output [key] = String(value)
            step2.removeFirst(index + 2)

            } while step2.count != 0

            output.forEach{
            print("($0.key): ($0.value)")
            }
            }


            // Output:
            // Host: somesite.com
            // Status Connection: deny
            // Status: N/A
            // heart beat: dead
            // is Connection live: true


            Edit: Array.firstIndex(where:) seems to be Xcode10+, so in Xcode9.4 you may try:



            extension Array {
            func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Int? {
            for index in indices where try predicate(self[index]) { return index }
            return nil
            }
            }


            Edit2: A backward-compatible way to add .firstIndex(where:) from Leo:



            extension Collection {
            func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
            return try index(where: predicate)
            }
            }





            share|improve this answer















            The thing what makes this parsable is, that each value is only a single word and that key and value are separated by :.



            First I identify the the end of the next key, then add the next word as its value.



            func testing() {
            let input = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

            var step1 = input.split(separator: " ")
            guard let index = step1.firstIndex(where: { $0.contains(",") }) else {
            fatalError("Does not contain `,`. at the beginning.")
            }
            step1.removeFirst(index + 1)

            var step2 = step1
            var output = [String: String]()
            repeat {
            guard let index = step2.firstIndex(where: { $0.contains(":") }) else {
            // If the last part has no :, can add it under `end`.
            // output["end"] = step2.joined(separator: " ")
            step2.removeAll()
            break
            }
            let key = step2[0...index].joined(separator: " ").trimmingCharacters(in: CharacterSet(charactersIn: ":"))
            let value = step2[index + 1]
            output [key] = String(value)
            step2.removeFirst(index + 2)

            } while step2.count != 0

            output.forEach{
            print("($0.key): ($0.value)")
            }
            }


            // Output:
            // Host: somesite.com
            // Status Connection: deny
            // Status: N/A
            // heart beat: dead
            // is Connection live: true


            Edit: Array.firstIndex(where:) seems to be Xcode10+, so in Xcode9.4 you may try:



            extension Array {
            func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Int? {
            for index in indices where try predicate(self[index]) { return index }
            return nil
            }
            }


            Edit2: A backward-compatible way to add .firstIndex(where:) from Leo:



            extension Collection {
            func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
            return try index(where: predicate)
            }
            }






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Aug 24 '18 at 17:53

























            answered Aug 24 '18 at 17:06









            FabianFabian

            1,1542514




            1,1542514













            • Thanks Purpose. I'm unable to see step1.firstIndex. Gettting error in Playground string-operations.playground:161:27: error: value of type '[String.SubSequence]' (aka 'Array<Substring>') has no member 'firstIndex' guard let index = step1.firstIndex(where: { $0.contains(":") }) else {

              – iamMobile
              Aug 24 '18 at 17:16













            • change it index(of:) and index(where:)

              – Leo Dabus
              Aug 24 '18 at 17:27













            • @iamMobile The function seems to have been added in Xcode10/Swift4.2, try the extension above for an alternative. Or try Leo's tip. I can't find them in the Array docs but they may have removed deprecated/renamed functions from it.

              – Fabian
              Aug 24 '18 at 17:30








            • 1





              @Purpose better to use the array indices property to iterate through it. for index in indices where try predicate(self[index]) { return index }

              – Leo Dabus
              Aug 24 '18 at 17:52








            • 1





              @LeoDabus Damn this makes things short, very nice!

              – Fabian
              Aug 24 '18 at 17:55



















            • Thanks Purpose. I'm unable to see step1.firstIndex. Gettting error in Playground string-operations.playground:161:27: error: value of type '[String.SubSequence]' (aka 'Array<Substring>') has no member 'firstIndex' guard let index = step1.firstIndex(where: { $0.contains(":") }) else {

              – iamMobile
              Aug 24 '18 at 17:16













            • change it index(of:) and index(where:)

              – Leo Dabus
              Aug 24 '18 at 17:27













            • @iamMobile The function seems to have been added in Xcode10/Swift4.2, try the extension above for an alternative. Or try Leo's tip. I can't find them in the Array docs but they may have removed deprecated/renamed functions from it.

              – Fabian
              Aug 24 '18 at 17:30








            • 1





              @Purpose better to use the array indices property to iterate through it. for index in indices where try predicate(self[index]) { return index }

              – Leo Dabus
              Aug 24 '18 at 17:52








            • 1





              @LeoDabus Damn this makes things short, very nice!

              – Fabian
              Aug 24 '18 at 17:55

















            Thanks Purpose. I'm unable to see step1.firstIndex. Gettting error in Playground string-operations.playground:161:27: error: value of type '[String.SubSequence]' (aka 'Array<Substring>') has no member 'firstIndex' guard let index = step1.firstIndex(where: { $0.contains(":") }) else {

            – iamMobile
            Aug 24 '18 at 17:16







            Thanks Purpose. I'm unable to see step1.firstIndex. Gettting error in Playground string-operations.playground:161:27: error: value of type '[String.SubSequence]' (aka 'Array<Substring>') has no member 'firstIndex' guard let index = step1.firstIndex(where: { $0.contains(":") }) else {

            – iamMobile
            Aug 24 '18 at 17:16















            change it index(of:) and index(where:)

            – Leo Dabus
            Aug 24 '18 at 17:27







            change it index(of:) and index(where:)

            – Leo Dabus
            Aug 24 '18 at 17:27















            @iamMobile The function seems to have been added in Xcode10/Swift4.2, try the extension above for an alternative. Or try Leo's tip. I can't find them in the Array docs but they may have removed deprecated/renamed functions from it.

            – Fabian
            Aug 24 '18 at 17:30







            @iamMobile The function seems to have been added in Xcode10/Swift4.2, try the extension above for an alternative. Or try Leo's tip. I can't find them in the Array docs but they may have removed deprecated/renamed functions from it.

            – Fabian
            Aug 24 '18 at 17:30






            1




            1





            @Purpose better to use the array indices property to iterate through it. for index in indices where try predicate(self[index]) { return index }

            – Leo Dabus
            Aug 24 '18 at 17:52







            @Purpose better to use the array indices property to iterate through it. for index in indices where try predicate(self[index]) { return index }

            – Leo Dabus
            Aug 24 '18 at 17:52






            1




            1





            @LeoDabus Damn this makes things short, very nice!

            – Fabian
            Aug 24 '18 at 17:55





            @LeoDabus Damn this makes things short, very nice!

            – Fabian
            Aug 24 '18 at 17:55











            1














            Since you have fixed keys and response format, it might be better go with specific parsing logics with your keys.



            let response = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

            enum ResponseParseId: String {
            case status = "Status:"
            case host = "Host:"
            case isConnectionLive = "is Connection live:"
            case statusConnection = "Status Connection:"
            case heartBeat = "heart beat:"

            var key: String {
            switch self {
            case .status:
            return "Status"
            case .host:
            return "Host"
            case .isConnectionLive:
            return "is Connection live"
            case .statusConnection:
            return "Status Connection"
            case .heartBeat:
            return "heart beat"
            }
            }

            static var allIds: [ResponseParseId] = [.status, .host, .isConnectionLive, .statusConnection, .heartBeat]
            }

            func getValue(from response: String, for key: ResponseParseId) -> String? {
            let components = response.components(separatedBy: key.rawValue)
            if let substring = components.last?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) {
            let subcomponents = substring.components(separatedBy: " ")
            if let value = subcomponents.first {
            return value
            }
            }
            return nil
            }

            func getResponseDictionary(response: String) -> [String: String] {
            var dictionary: [String: String] = [:]
            for id in ResponseParseId.allIds {
            if let value = getValue(from: response, for: id) {
            dictionary[id.key] = value
            }
            }
            return dictionary
            }

            let dictionary = getResponseDictionary(response: response)

            print(dictionary)





            share|improve this answer
























            • Interesting approach! Since unknown keys can't be used anyway it makes sense to define them, makes things more sturdy and easier to use with -> [ResponseParseID: String].

              – Fabian
              Aug 24 '18 at 21:49
















            1














            Since you have fixed keys and response format, it might be better go with specific parsing logics with your keys.



            let response = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

            enum ResponseParseId: String {
            case status = "Status:"
            case host = "Host:"
            case isConnectionLive = "is Connection live:"
            case statusConnection = "Status Connection:"
            case heartBeat = "heart beat:"

            var key: String {
            switch self {
            case .status:
            return "Status"
            case .host:
            return "Host"
            case .isConnectionLive:
            return "is Connection live"
            case .statusConnection:
            return "Status Connection"
            case .heartBeat:
            return "heart beat"
            }
            }

            static var allIds: [ResponseParseId] = [.status, .host, .isConnectionLive, .statusConnection, .heartBeat]
            }

            func getValue(from response: String, for key: ResponseParseId) -> String? {
            let components = response.components(separatedBy: key.rawValue)
            if let substring = components.last?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) {
            let subcomponents = substring.components(separatedBy: " ")
            if let value = subcomponents.first {
            return value
            }
            }
            return nil
            }

            func getResponseDictionary(response: String) -> [String: String] {
            var dictionary: [String: String] = [:]
            for id in ResponseParseId.allIds {
            if let value = getValue(from: response, for: id) {
            dictionary[id.key] = value
            }
            }
            return dictionary
            }

            let dictionary = getResponseDictionary(response: response)

            print(dictionary)





            share|improve this answer
























            • Interesting approach! Since unknown keys can't be used anyway it makes sense to define them, makes things more sturdy and easier to use with -> [ResponseParseID: String].

              – Fabian
              Aug 24 '18 at 21:49














            1












            1








            1







            Since you have fixed keys and response format, it might be better go with specific parsing logics with your keys.



            let response = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

            enum ResponseParseId: String {
            case status = "Status:"
            case host = "Host:"
            case isConnectionLive = "is Connection live:"
            case statusConnection = "Status Connection:"
            case heartBeat = "heart beat:"

            var key: String {
            switch self {
            case .status:
            return "Status"
            case .host:
            return "Host"
            case .isConnectionLive:
            return "is Connection live"
            case .statusConnection:
            return "Status Connection"
            case .heartBeat:
            return "heart beat"
            }
            }

            static var allIds: [ResponseParseId] = [.status, .host, .isConnectionLive, .statusConnection, .heartBeat]
            }

            func getValue(from response: String, for key: ResponseParseId) -> String? {
            let components = response.components(separatedBy: key.rawValue)
            if let substring = components.last?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) {
            let subcomponents = substring.components(separatedBy: " ")
            if let value = subcomponents.first {
            return value
            }
            }
            return nil
            }

            func getResponseDictionary(response: String) -> [String: String] {
            var dictionary: [String: String] = [:]
            for id in ResponseParseId.allIds {
            if let value = getValue(from: response, for: id) {
            dictionary[id.key] = value
            }
            }
            return dictionary
            }

            let dictionary = getResponseDictionary(response: response)

            print(dictionary)





            share|improve this answer













            Since you have fixed keys and response format, it might be better go with specific parsing logics with your keys.



            let response = "Result Set, Status: N/A Host: somesite.com is Connection live: true Status Connection: deny heart beat: dead"

            enum ResponseParseId: String {
            case status = "Status:"
            case host = "Host:"
            case isConnectionLive = "is Connection live:"
            case statusConnection = "Status Connection:"
            case heartBeat = "heart beat:"

            var key: String {
            switch self {
            case .status:
            return "Status"
            case .host:
            return "Host"
            case .isConnectionLive:
            return "is Connection live"
            case .statusConnection:
            return "Status Connection"
            case .heartBeat:
            return "heart beat"
            }
            }

            static var allIds: [ResponseParseId] = [.status, .host, .isConnectionLive, .statusConnection, .heartBeat]
            }

            func getValue(from response: String, for key: ResponseParseId) -> String? {
            let components = response.components(separatedBy: key.rawValue)
            if let substring = components.last?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) {
            let subcomponents = substring.components(separatedBy: " ")
            if let value = subcomponents.first {
            return value
            }
            }
            return nil
            }

            func getResponseDictionary(response: String) -> [String: String] {
            var dictionary: [String: String] = [:]
            for id in ResponseParseId.allIds {
            if let value = getValue(from: response, for: id) {
            dictionary[id.key] = value
            }
            }
            return dictionary
            }

            let dictionary = getResponseDictionary(response: response)

            print(dictionary)






            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Aug 24 '18 at 19:51









            HMHeroHMHero

            1,920129




            1,920129













            • Interesting approach! Since unknown keys can't be used anyway it makes sense to define them, makes things more sturdy and easier to use with -> [ResponseParseID: String].

              – Fabian
              Aug 24 '18 at 21:49



















            • Interesting approach! Since unknown keys can't be used anyway it makes sense to define them, makes things more sturdy and easier to use with -> [ResponseParseID: String].

              – Fabian
              Aug 24 '18 at 21:49

















            Interesting approach! Since unknown keys can't be used anyway it makes sense to define them, makes things more sturdy and easier to use with -> [ResponseParseID: String].

            – Fabian
            Aug 24 '18 at 21:49





            Interesting approach! Since unknown keys can't be used anyway it makes sense to define them, makes things more sturdy and easier to use with -> [ResponseParseID: String].

            – Fabian
            Aug 24 '18 at 21:49


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52008779%2fsplitting-swift-string-complex%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Guess what letter conforming each word

            Port of Spain

            Run scheduled task as local user group (not BUILTIN)