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.