My URL in Kotlin has an extra & at the end of it - dictionary

private val BASE_URL = "https://itunes.apple.com/search?"
private var iTunesMap = mutableMapOf("term" to "parameter1",
"media" to "parameter2")
fun URL.addParameters(map:Map<String,String>) : String {
var newURL = BASE_URL
for(parameter in iTunesMap.iterator())
{
newURL += parameter.key + "=" + parameter.value + "&"
}
return newURL
}
I am ending up with a & at the end of the URL.
How do I prevent this from happening?

You can simply do it in a functional way:
val parameters = map.map { (key, value) -> // 1
"$key=$value" // 2
}.joinToString("&") // 3
return BASE_URL + parameters // 4
Explenation:
We use map function with destructuring declaration of parameters (key, value) which correspond to key and value in each map entry
For each map entry we create string key=value ($ is indicator, that we want to use value of some variable - in that case we use value of parameter key and parameter value
After .map function we have collection of strings: term=parameter1 and media=parameter2, we are joining them with & separator
Lastly we need to concatenate parameters to base url
If I can suggest - I would make this function more general by putting baseUrl as parameter - then you can use the same function for different urls.
EDIT
As Михаил Нафталь suggested in his answer, you can add prefix to joinToString function so you don't have to concatenate parameters with base url. So after that improvement your code will look like this:
return map.map { (key, value) ->
"$key=$value"
}.joinToString("&", prefix = BASE_URL )
Or you can do all of this using just joinToString function with giving transform function. (look for Михаил Нафталь answer)

Here is a one-liner:
map.entries.joinToString(separator = "&", prefix = BASE_URL) { (key, value) -> "$key=$value" }

Strings are immutable and += involves copying the entire string into a new string. Do not use += on a var: String in a loop, as that is O(N²); use a StringBuilder instead.
val urlBuilder = StringBuilder(BASE_URL)
iTunesMap.entries.joinTo(urlBuilder, separator = "&") { (key, value) ->
"$key=$value"
}
return urlBuilder.toString()

Related

How to transform string "key:value" in actual map?

Is it possible to transform a string containing "key:value key2:value" in an actual Golang map?
Example:
package main
type Test struct{
options map[string]interface{}
}
func main() {
string := "key:value key2:key:value"
var test Test
/// Do something to parse string into test.Options
}
Thanks!
Yes, you can definitely parse that string into a map of the type you specify!
Take a look at the "strings" package, in particular strings.SplitN(...). Consider these examples and think about how you can combine them, along with iteration, to populate that map:
str := "key:value key2:key:value"
kvs := strings.SplitN(str, " ", -1)
// []string{
// "key:value",
// "key2:key:value",
// }
kv1 := strings.SplitN(kvs[1], ":", 2)
// []string{
// "key2",
// "key:value",
// }
That is, you can split the string by spaces () to get each key/value pair, then you can split by colons (:) to split each pair into their own parts. Finally, you just need to add each pair to the map!
Notice that when you call strings.SplitN(...) with -1 as the final argument it will split the input string into as many substrings as are present:
kv1 := strings.SplitN(kvs[1], ":", -1) // Note the -1 here
// []string{
// "key2",
// "key",
// "value",
// }

Apply changes to Map values using Java 8 Streams

I want to change Map values using java 8.
Map<String, String> attributeMap = new TreeMap<>();
attributeMap.put("C","FIRSTNAMe");
attributeMap.put("C3","1111");
attributeMap.put("C4","ABCNAMe");
After the changes, the output as a string should look like
c='FIRSTNAMe',c3=111,c4='ABCNAMe'
Can anyone help me.
You could create a method transform and pass a Predicate for when to quote or not:
public static String tranform(Map<String, String> attributeMap, Predicate<String> predicate) {
return attributeMap.entrySet()
.stream()
.collect(Collectors.mapping(e -> {
return e.getKey().toLowerCase() + "=" +
(predicate.test(e.getValue()) ? "'" + e.getValue() + "'" : e.getValue());
}, Collectors.joining(",")));
}
And call it like this:
String result = tranform(attributeMap, "1111"::equals);
System.out.println(result); // c=FIRSTNAMe,c3='1111',c4=ABCNAMe
If you want to quote only numbers, just use a different Predicate:
Predicate<String> predicate = s -> s.matches("\\d+");
String result = tranform(attributeMap, predicate);

Custom sort file name string array

I'm retrieving a string array of files and I would like to custom sort them by a substring in the file name...using C# **.NET 3.5. Below is what I am working with.
<% string[] files = System.IO.Directory.GetFiles("...path..." + pageName + "\\reference\\");
files = String.Join(",", files).Replace("...path...", "").Replace("\\reference\\", "").Replace(pageName, "").Split(new Char[] { ',' });
foreach (String item in files)
{
Response.Write("<a href=" + pageName + "/reference/" + System.IO.Path.GetFileName(item) + " target='_blank'>" + item.Replace("_", " ").Replace(".pdf", " ") + "</a>");
}
%>
I'm a C# noob, and I don't know where to go from here. Basically, I'm looking for a substring in the file name to determine the order (e.g., "index","reference","list"; where any file including the string "index" would be listed first). Perhaps there is a better way to do it. Any help would be appreciated.
You can use Linq to order the array by the filenames. In general, use the Path class if you're working with paths.
string fullPath = Path.Combine(directory, pageName, "reference");
var filePaths = Directory.EnumerateFiles(fullPath, "*.*", SearchOption.TopDirectoryOnly)
.Select(fp => new{ FullPath = fp, FileName=Path.GetFileName(fp) })
.OrderByDescending(x => x.FileName.IndexOf("index", StringComparison.OrdinalIgnoreCase) >= 0)
.ThenByDescending(x => x.FileName.IndexOf("reference", StringComparison.OrdinalIgnoreCase) >= 0)
.ThenByDescending(x => x.FileName.IndexOf("list", StringComparison.OrdinalIgnoreCase) >= 0)
.ThenBy(x=> x.FileName)
.Select(x => x.FullPath);
foreach(string filePath in filePaths)
;// ...
If you don't want to compare case-insensitively (so that "index" and "Index" are considered the same) use String.Contains instead of String.IndexOf + StringComparison.OrdinalIgnoreCase.
Here's an easy way I use when I run into this problem.
Define the order of the substrings in a list. Then for each item, check to see whats the first thing that contains that item. Then sort by the order of the substring in the list.
public class SubStringSorter : IComparer<string>
{
public int Compare(string x, string y)
{
var source = x.ToLowerInvariant();
var target = y.ToLowerInvariant();
var types = new List<string> { "base", "data", "model", "services", "interceptor", "controllers", "directives", "filters", "app", "tests", "unittests" };
var sourceType = types.IndexOf(types.FirstOrDefault(source.Contains));
var targetType = types.IndexOf(types.FirstOrDefault(target.Contains));
return sourceType.CompareTo(targetType);
}
}
To sort your files, do something like
var list = new List<string>{ "baseFile", "servicesFile", "this ModElstuff" };
list.Sort(new SubStringSorter());
And the output
You could even go one step further and give the substring sorter the list as part of its constructor so you can re-use the substring sort order with other items. The example I posted tests if the string exists in any context, but if you are more interested in it starting with a string you can do that too.

How to parse query strings in Dart?

How do I parse query strings safely in Dart?
Let's assume I have q string with the value of:
?page=main&action=front&sid=h985jg9034gj498g859gh495
Ideally the code should work both in the server and client, but for now I'll settle for a working client-side code.
The simpler, the better. Look for the splitQueryString static method of class Uri.
Map<String, String> splitQueryString(String query, {Encoding encoding: UTF8})
Returns the query split into a map according to the rules specified for
FORM post in the HTML 4.01 specification section 17.13.4. Each key and value
in the returned map has been decoded. If the query is the empty string an
empty map is returned.
I have made a simple package for that purpose exactly: https://github.com/kaisellgren/QueryString
Example:
import 'package:query_string/query_string.dart');
void main() {
var q = '?page=main&action=front&sid=h985jg9034gj498g859gh495&enc=+Hello%20&empty';
var r = QueryString.parse(q);
print(r['page']); // "main"
print(r['asdasd']); // null
}
The result is a Map. Accessing parameters is just a simple r['action'] and accessing a non-existant query parameter is null.
Now, to install, add to your pubspec.yaml as a dependency:
dependencies:
query_string: any
And run pub install.
The library also handles decoding of things like %20 and +, and works even for empty parameters.
It does not support "array style parameters", because they are not part of the RFC 3986 specification.
I done that just like this:
Map<String, String> splitQueryString(String query) {
return query.split("&").fold({}, (map, element) {
int index = element.indexOf("=");
if (index == -1) {
if (element != "") {
map[element] = "";
}
} else if (index != 0) {
var key = element.substring(0, index);
var value = element.substring(index + 1);
map[key] = value;
}
return map;
});
}
I took it from splitQueryString

NameValueCollection to URL Query?

I know i can do this
var nv = HttpUtility.ParseQueryString(req.RawUrl);
But is there a way to convert this back to a url?
var newUrl = HttpUtility.Something("/page", nv);
Simply calling ToString() on the NameValueCollection will return the name value pairs in a name1=value1&name2=value2 querystring ready format. Note that NameValueCollection types don't actually support this and it's misleading to suggest this, but the behavior works here due to the internal type that's actually returned, as explained below.
Thanks to #mjwills for pointing out that the HttpUtility.ParseQueryString method actually returns an internal HttpValueCollection object rather than a regular NameValueCollection (despite the documentation specifying NameValueCollection). The HttpValueCollection automatically encodes the querystring when using ToString(), so there's no need to write a routine that loops through the collection and uses the UrlEncode method. The desired result is already returned.
With the result in hand, you can then append it to the URL and redirect:
var nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());
string url = Request.Url.AbsolutePath + "?" + nameValues.ToString();
Response.Redirect(url);
Currently the only way to use a HttpValueCollection is by using the ParseQueryString method shown above (other than reflection, of course). It looks like this won't change since the Connect issue requesting this class be made public has been closed with a status of "won't fix."
As an aside, you can call the Add, Set, and Remove methods on nameValues to modify any of the querystring items before appending it. If you're interested in that see my response to another question.
string q = String.Join("&",
nvc.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(nvc[a])));
Make an extension method that uses a couple of loops. I prefer this solution because it's readable (no linq), doesn't require System.Web.HttpUtility, and it supports duplicate keys.
public static string ToQueryString(this NameValueCollection nvc)
{
if (nvc == null) return string.Empty;
StringBuilder sb = new StringBuilder();
foreach (string key in nvc.Keys)
{
if (string.IsNullOrWhiteSpace(key)) continue;
string[] values = nvc.GetValues(key);
if (values == null) continue;
foreach (string value in values)
{
sb.Append(sb.Length == 0 ? "?" : "&");
sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
}
}
return sb.ToString();
}
Example
var queryParams = new NameValueCollection()
{
{ "order_id", "0000" },
{ "item_id", "1111" },
{ "item_id", "2222" },
{ null, "skip entry with null key" },
{ "needs escaping", "special chars ? = &" },
{ "skip entry with null value", null }
};
Console.WriteLine(queryParams.ToQueryString());
Output
?order_id=0000&item_id=1111&item_id=2222&needs%20escaping=special%20chars%20%3F%20%3D%20%26
This should work without too much code:
NameValueCollection nameValues = HttpUtility.ParseQueryString(String.Empty);
nameValues.Add(Request.QueryString);
// modify nameValues if desired
var newUrl = "/page?" + nameValues;
The idea is to use HttpUtility.ParseQueryString to generate an empty collection of type HttpValueCollection. This class is a subclass of NameValueCollection that is marked as internal so that your code cannot easily create an instance of it.
The nice thing about HttpValueCollection is that the ToString method takes care of the encoding for you. By leveraging the NameValueCollection.Add(NameValueCollection) method, you can add the existing query string parameters to your newly created object without having to first convert the Request.QueryString collection into a url-encoded string, then parsing it back into a collection.
This technique can be exposed as an extension method as well:
public static string ToQueryString(this NameValueCollection nameValueCollection)
{
NameValueCollection httpValueCollection = HttpUtility.ParseQueryString(String.Empty);
httpValueCollection.Add(nameValueCollection);
return httpValueCollection.ToString();
}
Actually, you should encode the key too, not just value.
string q = String.Join("&",
nvc.AllKeys.Select(a => $"{HttpUtility.UrlEncode(a)}={HttpUtility.UrlEncode(nvc[a])}"));
Because a NameValueCollection can have multiple values for the same key, if you are concerned with the format of the querystring (since it will be returned as comma-separated values rather than "array notation") you may consider the following.
Example
var nvc = new NameValueCollection();
nvc.Add("key1", "val1");
nvc.Add("key2", "val2");
nvc.Add("empty", null);
nvc.Add("key2", "val2b");
Turn into: key1=val1&key2[]=val2&empty&key2[]=val2b rather than key1=val1&key2=val2,val2b&empty.
Code
string qs = string.Join("&",
// "loop" the keys
nvc.AllKeys.SelectMany(k => {
// "loop" the values
var values = nvc.GetValues(k);
if(values == null) return new[]{ k };
return nvc.GetValues(k).Select( (v,i) =>
// 'gracefully' handle formatting
// when there's 1 or more values
string.Format(
values.Length > 1
// pick your array format: k[i]=v or k[]=v, etc
? "{0}[]={1}"
: "{0}={1}"
, k, HttpUtility.UrlEncode(v), i)
);
})
);
or if you don't like Linq so much...
string qs = nvc.ToQueryString(); // using...
public static class UrlExtensions {
public static string ToQueryString(this NameValueCollection nvc) {
return string.Join("&", nvc.GetUrlList());
}
public static IEnumerable<string> GetUrlList(this NameValueCollection nvc) {
foreach(var k in nvc.AllKeys) {
var values = nvc.GetValues(k);
if(values == null) { yield return k; continue; }
for(int i = 0; i < values.Length; i++) {
yield return
// 'gracefully' handle formatting
// when there's 1 or more values
string.Format(
values.Length > 1
// pick your array format: k[i]=v or k[]=v, etc
? "{0}[]={1}"
: "{0}={1}"
, k, HttpUtility.UrlEncode(values[i]), i);
}
}
}
}
As has been pointed out in comments already, with the exception of this answer most of the other answers address the scenario (Request.QueryString is an HttpValueCollection, "not" a NameValueCollection) rather than the literal question.
Update: addressed null value issue from comment.
The short answer is to use .ToString() on the NameValueCollection and combine it with the original url.
However, I'd like to point out a few things:
You cant use HttpUtility.ParseQueryString on Request.RawUrl. The ParseQueryString() method is looking for a value like this: ?var=value&var2=value2.
If you want to get a NameValueCollection of the QueryString parameters just use Request.QueryString().
var nv = Request.QueryString;
To rebuild the URL just use nv.ToString().
string url = String.Format("{0}?{1}", Request.Path, nv.ToString());
If you are trying to parse a url string instead of using the Request object use Uri and the HttpUtility.ParseQueryString method.
Uri uri = new Uri("<THE URL>");
var nv = HttpUtility.ParseQueryString(uri.Query);
string url = String.Format("{0}?{1}", uri.AbsolutePath, nv.ToString());
I always use UriBuilder to convert an url with a querystring back to a valid and properly encoded url.
var url = "http://my-link.com?foo=bar";
var uriBuilder = new UriBuilder(url);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query.Add("yep", "foo&bar");
uriBuilder.Query = query.ToString();
var result = uriBuilder.ToString();
// http://my-link.com:80/?foo=bar&yep=foo%26bar
In AspNet Core 2.0 you can use QueryHelpers AddQueryString method.
As #Atchitutchuk suggested, you can use QueryHelpers.AddQueryString in ASP.NET Core:
public string FormatParameters(NameValueCollection parameters)
{
var queryString = "";
foreach (var key in parameters.AllKeys)
{
foreach (var value in parameters.GetValues(key))
{
queryString = QueryHelpers.AddQueryString(queryString, key, value);
}
};
return queryString.TrimStart('?');
}
This did the trick for me:
public ActionResult SetLanguage(string language = "fr_FR")
{
Request.UrlReferrer.TryReadQueryAs(out RouteValueDictionary parameters);
parameters["language"] = language;
return RedirectToAction("Index", parameters);
}
You can use.
var ur = new Uri("/page",UriKind.Relative);
if this nv is of type string you can append to the uri first parameter.
Like
var ur2 = new Uri("/page?"+nv.ToString(),UriKind.Relative);

Resources