I've been trying to remove the particular query (ex: "remove=me") from the URL but it seems like there is no API for it
use url::{Url};
fn main(){
let mut url = Url::parse("https://www.example.com/foo?id=1&remove=me").unwrap();
url.set_query(None); // This will remove all the query parameters
println!("{}", URL);
// expected result: https://www.example.com/foo?id=1
// actual result: https://www.example.com/foo
}
Indeed, the url crate makes it quite awkward. The query string can only be appended to or cleared. Editing it requires some gymnastics: generating a filtered set of name/value pairs, then clearing and extended the query string.
let mut url = Url::parse("https://www.example.com/foo?id=1&remove=me").unwrap();
let query: Vec<(String, String)> = url
.query_pairs()
.filter(|(name, _)| name != "remove")
.map(|(name, value)| (name.into_owned(), value.into_owned()))
.collect();
url.query_pairs_mut().clear().extend_pairs(&query);
println!("{}", url);
Playground
Related
I want to write a parser.
It seems practical to me to have a mutable Iterator that I can pass around to different parser functions.
I've tried to illustrated a simplified approach, which compiles but is not ideal yet.
fn main() {
let tokens = vec!["fIrSt".to_string(), "SeConD".to_string(), "tHiRd".to_string(), "FoUrTh".to_string()];
let parsed = parse_input(tokens);
println!("{}", parsed);
}
fn parse_input(tokens: Vec<String>) -> String {
let mut tokens_iter = tokens.iter();
let upps = parse_upper(&mut tokens_iter);
let lowers = parse_lower(&mut tokens_iter);
upps + &lowers
}
fn parse_upper(tokens_iter: &mut Iterator<Item=&String>) -> String {
let mut result = String::new();
let token_1 = tokens_iter.next().unwrap().to_uppercase();
let token_2 = tokens_iter.next().unwrap().to_uppercase();
result.push_str(&token_1);
result.push_str(&token_2);
result
}
fn parse_lower(tokens_iter: &mut Iterator<Item=&String>) -> String {
let mut result = String::new();
let token_1 = tokens_iter.next().unwrap().to_lowercase();
let token_2 = tokens_iter.next().unwrap().to_lowercase();
result.push_str(&token_1);
result.push_str(&token_2);
result
}
How the example works:
Let's say I have some input, that has already been tokenized. Here it is represented by the tokens vector (Vec<String>).
Inside the outer parse_input function, the Vec gets transformed into an Iterator and then passed into different, specific parser functions. Here: parse_upper and parse_lower. In real life those could be "parse_if_statement" or "parse_while_loop" but which part of the Iterator gets worked on is not relevant for the question.
What is relevant is, that every call to next advances the cursor on the Iterator. So that every function consumes the pieces it needs.
This example compiles and gives the output: FIRSTSECONDthirdfourth
I would like to be able to peek() into the Iterator, before I pass it to a function. This is necessary to determine which function should actually be called. But everything I have tried with using a Peekable instead of an Iterator resulted in total lifetime and borrow chaos.
Any suggestions on how to pass a Peekable instead of an Iterator in this case?
Maybe using a Peekable as function parameter is a bad idea in the first place. Or maybe my Iterator approach is already wrong. All suggestions/hints are welcome.
Hello I am trying to add queryItems to my URLComponents but because URLQueryItem returns its value as an optional the url keeps having a question mark in it.
var params = ["id":"5"]
let queryParams = toStringDictionary(dictionary: params).map { pair in
return URLQueryItem(name: pair.key, value: pair.value)
}
var components = URLComponents(string: fullURL)
components.queryItems = queryParams
print(components.url)//This is the full url https://demo.com/users/?5
//This https://demo.com/users/?5 should be this https://demo.com/users/5
The question mark is of course resulting in a wrong url. I can't get rid of it.
Query parameters are separated from the rest of the url by a question mark, for example:
https://example.com/path?param_name=param_value
The question mark is not due to an optional, but is the way that query params are supplied.
The example in your comment looks like it has lost the name of the param, I would expect it to read
https://demo.com/users/?id=5
If it definitely doesn't include the id= bit, please could you share the implementation of toStringDictionary so that we can see what's going on there? The dictionary you pass in is already a string dictionary, so seems odd to have such a function.
EDIT: following your clarifications (which have now disappeared, but made it clear that you were looking to add to the path rather than the query string)
It looks like your code is adding query parameters correctly; look closer at the URL it produces, it will be https://demo.com/users/?id=5. Query parameters are not part of the path, they come at the end and are separated from the rest of the URL by a ?
From your edits, it looks as though what you actually want to do is to add to the path of the URL, so that you get https://demo.com/users/5. That URL does not have any query parameters, so URLQueryItem is the wrong tool for the job.
To add to the path, you can do something like the following:
let userID = "5"
let url = URL(string: "https://example.com/users")!
let newUrl = url.appendingPathComponent(userID)
print(newUrl.absoluteString) //https://example.com/users/5
NB - url is only force unwrapped for brevity in the example, since the string passed to URL() is known to be valid. In real use, you should treat the optional more carefully.
There's something wrong with your toStringDictionary
Assuming
let fullURL = "https://demo.com/users/"
all you need to do is…
let queryParams = params.map{ URLQueryItem(name: $0, value: $1) }
var components = URLComponents(string: fullURL)!
components.queryItems = queryParams
print(components.url!)
// https://demo.com/users/?id=5
Note that you should avoid the force unwrapping in practice
I'm fetching some JSON data using URLSession.dataTask. To filter the data, I'm using a parameter that limits by date. The API uses square brackets for this, e.g.
https://foo.com/api/items.json?find[date]=....
However, upon creating a URL with the string and requesting data from it, the square brackets are automatically escaped:
https://foo.com/api/items.json?find%5Bdate%5D=...
With the brackets escaped, the API fails to recognize the request.
I've tried explicitly removing the percent encoding to create a new URL, i.e.
let unsanitized = URL(string: url.absoluteString.removingPercentEncoding)
but the percent sanitization persists.
I've spent a lot of time searching for a solution but haven't had any success—any suggestions?
Per the spec for URI: "A host identified by an Internet Protocol literal address, version 6 [RFC3513] or later, is distinguished by enclosing the IP literal within square brackets ("[" and "]")." rfc3986.
Thus you can't use square brackets for your purpose without escaping them. Your server's REST service is at fault for not handling the escaped characters in the query params. (And I've had situations in recent past where I've had to ask my REST team to fix this sort of problem where they forgot to support escaped query parameter values).
Try use percentEncodedQueryItems of URLComponents instead queryItems.
This is my codes in my app.
let fullUrl = baseURL.appendingPathComponent(path)
guard var components = URLComponents(url: fullUrl, resolvingAgainstBaseURL: false) else {
fatalError("Unable to create URL components")
}
var params = self.parameters // <-- parameters = [String:String]
params["serviceKey"] = Const.OPEN_API_KEY // <-- percent encoded key
components.percentEncodedQueryItems = params.map { // <-- already percent encoded Query Items
URLQueryItem(name: String($0), value: String($1))
}
guard let url = components.url else {
fatalError("Could not get url")
}
I have been trying to parse a CSV file forever and I am almost there. I have gotten it to a multi-dimensional array of strings using this code:
let path = Bundle.main.url(forResource: "BaseballSimStats", withExtension: "csv")
var file = String()
do {
file = try String(contentsOf: path!)
print(file)
} catch {
print(error)
}
let stringarray = file.components(separatedBy: "\n").map{ $0.components(separatedBy: ",") }
Now the last step is to turn it into a Double. I am using this code:
probs = Double[[stringarray]]
I get an error saying that the type has no subscript errors. I get rid of the subscript references and the error goes away. Why is this error here and how can I get rid of it? Thanks!
I used .map() to map the String into a Double, this should work for nested array
var strArray = [["1.00000","1.10000"],["2.00000","2.10000"]]
var doubleArray = strArray.map { (arr: Array) -> Array<Any> in
return arr.map({ (value: String) -> Double in
return Double(value)!
})
}
print(strArray)
print(doubleArray)
I am not sure if the double map was needed.
I am not a swift guru but this code should help you achieve what you want..
I'm not familiar with Double[[stringarray]] syntax so I don't know how that's supposed to work. I do know you can't just cast between array types.
The simplest way is probably to wrap the innermost call with Double.init():
file.components(separatedBy: "\n").map{ $0.components(separatedBy: ",").map { Double($0)! }}
Of course, there's a bit more to CSV than just splitting on commas and assuming everything is a valid number, so I'd highly recommend using an existing CSV parsing library for any real data.
I have an array of urls. I want to search for a specific element inside that array and if it's there remove it.
let urls: [URL?] = [url_0, url_A, url_Purple, etc..]
//first search for url_A
if urls.contains(where: url_A){
//second remove it from the array
urls = urls.filter{$0 != url_A}
}
I tried try and do-try-catch but they didn't work out. I know I'm doing this wrong and that's why I'm asking the question.
I get the error:
Cannot convert value of type 'URL' to expected arguement type '(URL)'
throws -> Bool
How do i search for a url inside an array of urls?
Based your question, you can do this:
let urls: [URL?] = [url_0, url_A, url_Purple, etc..]
//second remove it from the array
urls = urls.filter {$0 != url_A}
This will search for url_A and if found it will remove it and return new array. If I am correct, that is what you are looking for.
Based on the error which you got answer would be:
let urls: [URL?] = [url_0, url_A, url_Purple, etc..]
//first search for url_A
if urls.contains(where: {$0 == url_A} ) == true {
//second remove it from the array
urls = urls.filter{$0 != url_A}
}
Notice that it expects a closure as param in contains block. You were passing URL? type.