Home > Software design >  Get correct parameter value from URL in "/:id=" format using regex
Get correct parameter value from URL in "/:id=" format using regex

Time:09-21

What is the best way to get the id value from this url:

URL(string: "urlScheme://search/:id=0001")

I've been trying to route this URL using a deep link request. However, my url routing solution JLRoutes shows the parameters as key = id and value = :id=0001.

I instead need the parameters to be key = id and value = "0001".

In an ideal world I would just be using a URL string like "urlScheme://search/0001" and not have any problem but the ":id=" part has to be in there. George's comment about converting the parameter to a URL in of itself and using .pathComponents.last does work, but I think a regex solution is probably going to scale better going forward.

CodePudding user response:

If your URL is in the form of an actual URL query, e.g. urlScheme://search?id=0001, there is a nice way to do this.

With thanks to vadian, this is really simple. You can just do the following:

let components = URLComponents(string: "urlScheme://search?id=0001&a=2")!

let dict = components.queryItems?.reduce(into: [:]) { partialResult, queryItem in
    partialResult[queryItem.name] = queryItem.value
}

Or a slightly more compact version for dict:

let dict = components.queryItems?.reduce(into: [:], { $0[$1.name] = $1.value })

Result from given input:

["id": "0001", "a": "2"]

If you must use the current URL form

You can replace the URL string, such as:

let urlStr = "urlScheme://search/:id=0001/:a=2"
let comps = urlStr.components(separatedBy: "/:")

let newUrl: String

if comps.count > 1 {
    newUrl = "\(comps.first!)?\(comps.dropFirst().joined(separator: "&"))"
} else {
    newUrl = urlStr
}

print(newUrl)

Prints: urlScheme://search?id=0001&a=2


Original answer (slightly modified)

If you have a URL with queries separated by /: you can use the following:

// Example with multiple queries
let url = URL(string: "urlScheme://search/:id=0001/:a=2")!

let queries = url.lastPathComponent.dropFirst().split(separator: "/:")
var dict = [String: String]()

for query in queries {
    let splitQuery = query.split(separator: "=")
    guard splitQuery.count == 2 else { continue }
    let key = String(splitQuery.first!)
    let value = String(splitQuery[1])
    dict[key] = value
}

print(dict)

Result is same as before.

CodePudding user response:

The answer from @George should work fine, but two things struck me: you decided you wanted a regex solution, and to make this generic seemed to be asking for a recursive solution.

The below approach uses regex to identify up to the last /: delimiter, then has to do a bit of inelegant string handling to split it into the base string and the final pair of (key: value) params. I'd hoped to be able to write a regex that just matches those final parameters as that would be a far cleaner range to work with, but haven't managed it yet!

func paramsFrom(_ str: String) -> [String: String] {
   guard let baseRange = str.range(of:#"^. \/:"#, options: .regularExpression ) else { return [:] }
   let base = String(str[baseRange].dropLast(2))
   let params = str.replacingCharacters(in: baseRange, with: "").components(separatedBy: "=")
   return [params.first! : params.last!].merging(paramsFrom(base)){(current, _) in current}
}

using this on your example string returns:

["id": "0001", "title": "256", "count": "100"]

EDIT:

Managed to dig out the old regex brain cells and match just the final pair of parameters. You could adapt the above to use the regex

(?<=\/:)[a-zA-Z0-9=] $

and the have slightly cleaner string handling as the shortened base string becomes

String(str.dropLast(str[paramsRange].count))
  • Related