Home > OS >  How to fetch key, value pairs using swift
How to fetch key, value pairs using swift

Time:08-13

In the Swift file, write a program to read in the JSON object which contains a data key and the value is a string which contains items in the format: key=STRING, age=INTEGER. Your goal is to count how many items exist that have an age equal to or greater than 50, and print this final value

import Foundation

struct DataObject: Decodable {
    let data: String
}

let JSON = """
{"data":"key=IAfpK, age=58, key=WNVdi, age=64, key=jp9zt, age=47, key=0Sr4C, age=68, key=CGEqo, age=76, key=IxKVQ, age=79, key=eD221, age=29, key=XZbHV, age=32, key=k1SN5, key=zGtmR, age=66, key=nlIN9, age=8, key=hKalB, age=50, key=Na33O, age=17, key=jMeXm, age=15, key=OO2Mc, age=32, key=hhowx, age=34, key=gLMJf, age=60, key=PblX6, age=66, key=0iJGV, age=50, key=cFCfU, age=5, key=J8an1, age=48, key=dkSlj, age=5"}
"""

let jsonData = JSON.data(using: .utf8)!
let dataObject: DataObject = try! JSONDecoder().decode(DataObject.self, from: jsonData)
let r = dataObject.data.replacingOccurrences(of: ",", with:"" )

// totally lost here

print(dataObject.data)

CodePudding user response:

That's a good candidate for Regular Expression.

It checks for occurrences of "age=" followed by one or more digits and captures the numeric value.

The code enumerates the matches and increments a counter if the age is greater than 50

struct DataObject: Decodable {
    let data: String
}

let json = """
{"data":"key=IAfpK, age=58, key=WNVdi, age=64, key=jp9zt, age=47, key=0Sr4C, age=68, key=CGEqo, age=76, key=IxKVQ, age=79, key=eD221, age=29, key=XZbHV, age=32, key=k1SN5, key=zGtmR, age=66, key=nlIN9, age=8, key=hKalB, age=50, key=Na33O, age=17, key=jMeXm, age=15, key=OO2Mc, age=32, key=hhowx, age=34, key=gLMJf, age=60, key=PblX6, age=66, key=0iJGV, age=50, key=cFCfU, age=5, key=J8an1, age=48, key=dkSlj, age=5"}
"""

let jsonData = Data(json.utf8)
let value = try! JSONDecoder().decode(DataObject.self, from: jsonData).data

let regex = try! NSRegularExpression(pattern: #"age=(\d )"#)
var agesGreaterThan50 = 0
regex.enumerateMatches(in: value,
                       range: NSRange(json.startIndex..., in: value)) { (result, _ ,_)  in
    if let result = result,
       let range = Range(result.range(at: 1), in: value),
       let age = Int(value[range]), age > 50 {
        agesGreaterThan50  = 1
        
    }
}
print(agesGreaterThan50)

CodePudding user response:

We start with the given data string

let JSON = """
{
    "data": "key=IAfpK, age=58, key=WNVdi, age=64, key=jp9zt, age=47, key=0Sr4C, age=68, key=CGEqo, age=76, key=IxKVQ, age=79, key=eD221, age=29, key=XZbHV, age=32, key=k1SN5, key=zGtmR, age=66, key=nlIN9, age=8, key=hKalB, age=50, key=Na33O, age=17, key=jMeXm, age=15, key=OO2Mc, age=32, key=hhowx, age=34, key=gLMJf, age=60, key=PblX6, age=66, key=0iJGV, age=50, key=cFCfU, age=5, key=J8an1, age=48, key=dkSlj, age=5"
}
"""

This string needs to be converted to an instance of Data:

let jsonData = JSON.data(using: .utf16)! 

Note that I opt to use UTF16 here. This is important for NSRange which is a legacy old skool Objective-C class.

Next we can instantiate a new instance of a JSONDecoder:

let decoder = JSONDecoder()

And use it to decode an instance of our Ages struct (to follow below)

let ages = try decoder.decode(Ages.self, from: jsonData)

Report the results

print("There are \( ages.fiftyOrOlderCount ) keys aged 50 or older")
// prints out: There are 10 keys aged 50 or older

And finally, the implementation of Ages

struct Ages : Decodable {
    var fiftyOrOlderCount: Int

    init(from decoder: Decoder) throws {
        enum Keys : CodingKey { case data }
        fiftyOrOlderCount = 0

        let container     = try decoder.container(keyedBy: Keys.self) // {}
        let dataString    = try container.decode(String.self, forKey: Keys.data) // "key=IAfpK, age=58, k...
        let searchPattern = try NSRegularExpression(pattern: #"age=(\d )"#)
        let wholeString   = NSRange(location: 0, length: dataString.utf16.count)
        try searchPattern
            .matches(in: dataString, range: wholeString)
            .forEach { match in

            guard let range = Range(match.range(at:1), in: dataString)
            else { return } // should never happen unless Swift is bonkers

            let ageString = dataString[range] // extract the age

            guard let age = Int(ageString)
            else { throw DecodingError.dataCorrupted(.init(codingPath: [Keys.data], debugDescription: "Can't decode age at \(range)")) }

            guard age >= 50 else { return } // the great filter

            fiftyOrOlderCount  = 1
        }
    }
}

This implementation is a pretty standard way to decode unusual JSON data.

Note how wholeString counts the UTF16 characters in the data string.

The regular expression age=(\d ) looks for occurrences of the string "age=" followed by one or more numeric digits, and captures them using the round brackets.

While stepping through all the matches capturing group 0 represents the entire match, e.g. age=58 while group 1 is the bit in round brackets, e.g. 58

There's just enough error handling in this implementation to guard against malformed input JSON.

  • Related