I'm working on a project where I am given a user and a birthdate for them, along with a list of movies and dates that the person has gone to. An example string is something like this: "Participant Name: Example name, Birthdate: 01/11/2000, Spiderman 05/15/2021 07/16/2021 08/17/2021 Avengers Infinity War 05/15/2020 07/16/2020 08/17/2020 The Lorax 01/05/2015"
and so on. I know which movies the string will contain, and I know the maximum amount of dates per movie, but I don't know the specific number of times the person would have seen the movie. I've done the following for the birthdate, which works only because the request is always formatted the same way in terms of birthdates:
func FindBirthdate(str: String) -> Date{
//I make sure that the text before birthdate is always converted to DOB
//in other functions, and that the string is converted
//to an array seperated by spaces.
let index = str.firstIndex(of: "DOB:")!
let birthdate = str[index 1]
print(birthdate)
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yyyy"
formatter.locale = Locale(identifier: "COUNTRY IDENTIFIER")
formatter.timeZone = TimeZone(identifier: "EXAMPLE")
return formatter.date(from: birthdate) ?? Date()
}
However, as I previously stated, I don't know how many times the user would have seen the movie. The movies will all be in the same order. How would I split the characters in between each movie into dates for that movie? Once again, my problem is that I don't know the number of dates, so how would I get each date and return a list? I've looked into a sort of ForEach statement
, but I'm not sure how I could integrate that into a string. This answer suggested that I use regexes, however, this solely focuses on the dates, and not the movies. The string isn't solely made up of dates. I've also taken a look at sample date parsing in Swift, but that is about just single dates. My issue isn't date conversion, it's finding and separating the dates in the first place. On Meta someone also suggested that I try splitting. I have looked at Splitting on Apple Developer, and it seems like a good solution, but I'm not sure what I would split by. To show that sample string again, "Participant Name: Example name, Birthdate: 01/11/2000, Spiderman 05/15/2021 07/16/2021 08/17/2021 Avengers Infinity War 05/15/2020 07/16/2020 08/17/2020 The Lorax 01/05/2015"
. The movie names will always be only these - they will not ever have numbers. The dates will also always be in teh same MM/DD/YYYY format. The names are immediately before the dates, and there is no separator other than a space.
The reason that this question hasn't been asked before is that though other questions may ask about date parsing or finding substrings, I need to find each individual date for each movie and the movie title - this is trying to find each individual date in the string for each movie.
CodePudding user response:
Do this work for you? I am assuming you are exactly following the text format in your example.
extension String {
func match(_ regex: String) -> [[String]] {
let nsString = self as NSString
return (try? NSRegularExpression(pattern: regex, options: []))?.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)).map { match in
(0..<match.numberOfRanges).map { match.range(at: $0).location == NSNotFound ? "" : nsString.substring(with: match.range(at: $0)) }
} ?? []
}
}
Then:
func getName(text: String) -> String? {
guard let match = text.match("(?<=Participant Name: )(.*)(?=, Birthdate)").first else { return nil }
return match.first
}
func getBirthDay(text: String) -> String? {
guard let match = text.match("(?<=Birthdate: )(.*)(?=, )").first else { return nil }
return match.first
}
func getMovies(text: String) -> [String: [String]] {
var result: [String: [String]] = [:]
guard let moviesString = text.match("(?<=Participant Name: \(getName(text: text)!), Birthdate: \(getBirthDay(text: text)!), )(.*)").first?.first else { return result }
let asArray = moviesString.components(separatedBy: " ")
var key: String = ""
var values = [String]()
var lastKey: String = ""
for item in asArray {
if !isDate(item) {
values.removeAll()
key = key != "" ? (" " item) : item
lastKey = key
continue
} else {
key = ""
if var existingValues = result[lastKey] {
existingValues.append(item)
result[lastKey] = existingValues
} else {
result[lastKey] = [item]
}
}
}
return result
}
func isDate(_ string: String) -> Bool {
return !string.match("[0-9]{2}(/)[0-9]{2}(/)[0-9]{4}").isEmpty
}
To test:
let text = "Participant Name: Example name, Birthdate: 01/11/2000, Spiderman 05/15/2021 07/16/2021 08/17/2021 Avengers Infinity War 05/15/2020 07/16/2020 08/17/2020 The Lorax 01/05/2015"
let movies = getMovies(text: text)
print(">>>> \(movies["Spiderman"])")
output:
Optional(["05/15/2021", "07/16/2021", "08/17/2021"])