Is there a nice linqy way of splitting a FormCollection into a Dictionary<string,string> that contains only those keys that start with a certain string?
(This question is basically the same as this-> but for C#/FormCollection instead of python Slicing a dictionary by keys that start with a certain string)
Here's what I came up with to get around the problem:
public ActionResult Save(FormCollection formCollection) {
var appSettings = new Dictionary<string, string>();
var appKeys = formCollection.AllKeys.Where(k => k.StartsWith("AppSettings."));
foreach (var key in appKeys)
{
appSettings[key] = formCollection[key];
}
...
Edit: The problem with this code, is that I have to do it multiple times for different StartsWith strings, and will therefore need to create a 'utility' method to do the above.
It would be nice if it could read in one line like:
formCollection.Where(k=>k.Key.StartsWith("AppSettings.");
Background (not necessary to solve the problem): The context is asp.net mvc, and of a form with a dynamic dictionary of fields.
It's also similar to this question - Return FormCollection items with Prefix - but not quite the same.
And having read this answer How to build C# object from a FormCollection with complex keys - I started to wonder whether I'd be better off not even using form post, but sending JSON instead.
If you're looking for a "nice" way of taking an existing dictionary, producing a new dictionary with copies of keys+values, for a subset of the keys, some LINQ code will do this nicely:
var appSettings = formCollection.AllKeys
.Where(k => k.StartsWith("AppSettings."))
.ToDictionary(k => k, k => formCollection[k]);
[HttpPost]
public ActionResult Index(FormCollection collection)
{
Dictionary<string,object> form = new Dictionary<string, object>();
collection.CopyTo(form);
return View();
}
Related
I think the answer to this is pretty obvious, but it's friday so my brain isn't functioning entirely.
I'm working in an asp.net mvc application where I get the id of a selected row. Every time I click a row the id of that row is send to my controller.
What I want to do is output the id just as regular plain text.
This isn't too hard, I use this code:
public string GetInformation(int id)
{
return id.toString();
}
But the user can select multiple rows at once, what happens now is that the id just changes from the first selected row to the second one.
What I want is both the id's to be returned, separated by a comma.
This is what I tried:
public string[] GetInformation(int id)
{
List<string> oud = new List<string>();
oud.Add(id.ToString());
return oud.ToArray();
}
But then it just shows System.String[] as output.
Ok, I'm going to prefix this by saying that I think using sessions for state is generally a bad idea. However, it's an easy way to show state management and retaining the list between requests.
Update your function to this:
public string[] GetInformation(int id)
{
var list = Session["oud"] as List<string>;
if (list == null)
{
list = new List<string>();
Session["oud"] = list;
}
list.Add(id.ToString());
return list.ToArray();
}
Each time you call this method it will get the list from session (user state management). If the list returned from session is null (it's not actually there) we create a new list of strings and add it to the session.
To test this I put the following in an index action of an MVC controller before return View(). When I refresh the page I can see the random id being appended to the list. The same will apply if you make a call from the client.
Random r = new Random();
GetInformation(r.Next(1, 10));
Hope this helps!
Update
Iterating over an array in razor view and printing each item
In your razor view, if you have added this array to your model as MyIds, you could use the following:
#foreach (var id in Model.MyIds)
{
<p>#Html.Raw(id)</p>
}
Update 2
I'm going to make this very easy for you:
public string GetInformation(int id)
{
var list = Session["oud"] as List<string>;
if (list == null)
{
list = new List<string>();
Session["oud"] = list;
}
list.Add(id.ToString());
return string.Join(", ", list);
}
The function now returns a comma separated list of ids that you have passed through.
But then it just shows System.String[] as output.
Well, you're not showing us how you actually output anything. But presumably you're getting a value:
var output = GetInformation(input);
and then trying to write output directly as a string. However, string[] is an array and doesn't have a default string representation. All objects in .NET which don't have a .ToString() implementation inherit their implementation from System.Object, which defaults to outputting the type name.
Instead of outputting the object itself, which is semantically something like this:
Write(output)
(again, semantically, since we don't know how you're actually outputting it, so consider this pseudo-code)
Loop over it for your output:
foreach (var value in output)
Write(value)
You'll have to handle formatting (new lines, delimiters, etc.) for however you want to display the values as a UI concern. But the point, basically, is that you need to loop over your values and output them individually rather than as one big array.
There are shortcuts which will loop over it for you, if you'd like. For example:
Write(string.Join(",", output))
That would "join" all of the strings in output for you, using "," as a delimiter.
Edit: Another problem that you seem to be experiencing is that your method only ever returns a new list of exactly one object:
public string[] GetInformation(int id)
{
List<string> oud = new List<string>();
oud.Add(id.ToString());
return oud.ToArray();
}
This method shouldn't really have the responsibility of maintaining the list. This is because the method itself is stateless, all it really does is convert an integer value to a string. (Which you don't really need a method for, but whatever.) Consuming code should maintain state.
So keep the method as-is:
public string GetInformation(int id)
{
return id.toString();
}
And have the consuming code maintain the collection of values. Something like this:
var output = GetInformation(input);
myOutputs.Add(output);
Where is myOutputs defined? Well, where that state is maintained depends on a lot of things. This is a web application, so state can be an interesting thing. There are many places to maintain it:
Within the consuming method as a method-level variable
Within the consuming class as a class-level value
In session state
In a database
etc.
The overall flow of the logic and the application is going to govern this. For example, if a single instance of the class is maintaining the entire lifetime of this process then you would put it in a class-level value. However, if the value needs to persist across different page requests then you might want to put it in Session State instead. You have a number of options for where to maintain this collection of strings.
Try this:
PSEUDO CODE
public string[] GetInformation(int id)
{
List<string> oud = new List<string>();
oud.Add(id.ToString());
return oud.ToArray();
}
var myResult = GetInformation(1);
Console.WriteLine(string.Join(", ", myResult);
It's more nice to return unformatted data and format it when needed.
public int[] GetInformation(int id)
{
List<int> oud = new List<int>();
oud.Add(id);
return oud.ToArray();
}
var myResult = GetInformation(1);
Console.WriteLine(string.Join(", ", myResult);
This is probably a very simple question but google has let me down sofar and keeps pointing me towards python solutions.
I have a webpage where applciations/users can supply querystringparameters.To Retrieve the querystring parameters I use the following code:
IDictionary<string, string> qStrings = HtmlPage.Document.QueryString;
to check the presence of a specified key, I use the following code:
if (!String.IsNullOrEmpty(qStrings["PARAM1"]))
{}
Knowing our users, i'm expecting them to give parameterkeys as follows: "Param1", "param1", "pArAm1"
How can simply cast every key in a dictionary to uppercase without iterating each key-valuepair?
Or how can i alter the qStrings["PARAM1"] so it ignores the case?
You can use StringComparer to find keys ignoring their case:
var qStrings = new Dictionary<string, string>(
HtmlPage.Document.QueryString,
StringComparer.OrdinalIgnoreCase)
Simplest Way
qStrings = qStrings .ToDictionary(k => k.Key.ToUpper(), k => k.Value.ToUpper());
Maybe you can do it like below:
Dictionary<string, string> qStrings = new Dictionary<string, string>();
foreach (var a in qStrings.Keys)
{
switch (a.ToUpper())
{
case "PARAM1":
break;
}
}
Without iterating is not possible. No matter what approach you use there is going to be some sort of iteration. The this is you need to limit the insertion of the data to a single unified casing rather than allowing users to input all sorts of casing.
Taking your example: "Param1", "param1", "pArAm1", a key will be created for each single one of these as they are treated as separate entities. The best way to handle that is to force the casing at the insertion rather than when querying for values.
For example:
void AddToDictionary(string key, string value)
{
qStrings[key.ToUpper()] = value;
}
Coming from PHP, I have never written C# before and I have encountered something like this in C#:
public string rule(string playerId, string action, params string[] optionalData){
...
}
and in PHP it is like this
public function rule($playerId, $action, $optionalData=array()){
...
}
In PHP I simply fill out the parameter for the $optionalData like this...
myVar->rule("123", "myAction", array('url'=>'review.com');
However in C# I am not sure how to fill the optionalData (params string[] optionalData) parameter as it is a key value parameter (like in the PHP example). My question is how do I create a key value array like the PHP that I created in my example and put into the parameter?
CoolClass cc = new CoolClass();
cc.rule("123", "myAction", ???);
I was searching google and was looking at dictionary and hashmaps etc but I am guessing it is an overkill or it does not work..
Many thanks!
When you were looking at dictionaries, you were definitely looking at the right facility.
If rule() in C# is in your own code, may I recommend changing the signature to:
public string rule(string playerId, string action, IDictionary<string, string> optionalData = new Dictionary<string, string>()){
...
}
What this allows you to do:
Operate on the values in optionalData the way that other C# programmers will expect.
The = new Dictionary<string, string>() part of the suggested method signature make the parameter truly optional. It will not be necessary when calling the method.
You can use IDictionary<T> methods to work with the data. Some syntax you should be somewhat familiar with (consider accessing by key optionalData["someString"].)
However, if rule() is not in your code, you would leave out the optionalData by simply omitting parameters. Examples of valid calls of the original C# method in your question:
rule("Bob", "load")
rule("Bob", "load", "url", "www.example.com") (In this case, optionalData[0].Equals("url", StringComparisonOptions.Ordinal) and optionalData[1].Equals("www.example.com", StringComparisonOptions.Ordinal) is true.
One thing to consider about the original method - keep in mind that rule("Bob", "load", 'url") is a valid call, and you would need to have a run-time check to make sure you had the right number of parameters. Another plus to using a Dictionary<TKey, TValue>. You may even consider writing an adapter method to the original rule(), if you can't change it.
You can use a Dictionary:
Dictionary<string,string[]>
or something like:
Dictionary<int, string[]>
I believe dictionary will work in your case.
You can use Dictionary <key_datatype, value_datatype> .
Example:
Your method definition here :
public string rule(string playerId, string action, Dictionary<string, string> optionalData){
...
}
Method call:
Dictionary<string, string> optionalData = new Dictionary<string, string>();
optionalData.Add("url", "review.com");
cc.rule("123", "myAction", optionalData);
Or
you can use DynamoObject to make it more easier to write:
dynamic optionalData = new ExpandoObject();
//The token after the dynamoObject period will be the key to the assigned value.
optionalData.url = "review.com";
cc.rule("123", "myAction", optionalData);
Your method can get the key-value pairs like this:
public string rule(string playerId, string action, dynamic optionalData)
{
...
foreach (var pair in (IDictionary<string, object>)optionalData)
{
if (group.Key == "url")
{
Console.WriteLine(group.Value);
}
else if (group.Key == "post")
{
Console.WriteLine(group.Value);
}
}
}
I'm trying to create a list of strings (on a controller method for use as JSON that is consumed by a JQuery Autocomplete on the client). Is there a way to reduce these six or seven lines to two lines? In other words I want the first line to create the IEnumerable of strings.
Also is there a way of not using the custom comparer - all it does is compare strings (on the CompanyMeasureName field).
public JsonResult GetMyMeasureNameList(string term)
{
//I've defined a custom comparer called NameComparer on the MyMeasure Object
IEnumerable<MyMeasure> interList =
MyMeasure.Distinct(new MyMeasure.NameComparer())
.Where(cmo => cmo.CompanyMeasureName
.ToLower()
.Contains(term.ToLower()));
List<string> retList = new List<string>();
foreach (var cmo in interList.ToList())
{
CompanyMeasure c = (CompanyMeasure)cmo;
retList.Add(c.CompanyMeasureName);
}
return Json(retList, JsonRequestBehavior.AllowGet);
}
Thanks in advance
The following solves part of the problem (as usual I came up with the answer 5 seconds after posting the question). However I'd still like to be able to not use a custom comparer as it seems pretty pointless.
IEnumerable<MyMeasure> interList =
MyMeasure.Distinct(new MyMeasure.NameComparer())
.Where(cmo => cmo.CompanyMeasureName
.ToLower()
.Contains(term.ToLower())).Select(m => m.CompanyMeasure);
I have text documents like the following which contain single and multiple variables:
title:: Report #3
description:: This is the description.
note:: more information is available from marketing
note:: time limit for this project is 18 hours
todo:: expand the outline
todo:: work on the introduction
todo:: lookup footnotes
I need to iterate through the lines of this text document and fill a collection with these variables, currently I'm using a Dictionary:
public Dictionary<string, string> VariableNamesAndValues { get; set; }
But this doesn't work on multiple, identical keys such as "note" and "todo" in the above example since keys have to be unique in a Dictionary.
What is the best collection so that I can not only get single values like this:
string variableValue = "";
if (VariableNamesAndValues.TryGetValue("title", out variableValue))
return variableValue;
else
return "";
but that I can also get multiple values out like this:
//PSEUDO-CODE:
List<string> variableValues = new List<string>();
if (VariableNamesAndValues.TryGetValues("note", out variableValues))
return variableValues;
else
return null;
If your keys and values are strings then use a NameValueCollection. It supports multiple values for a given key.
It's not the most efficient collection in the world. Particularly because it's a non-generic class, uses a lot of virtual method calls, and the GetValues method will allocate arrays for its return values. But unless you require the best performing collection, this is certainly the most convenient collection that does what you ask.
You can make a Dictionary of key: string and value: List of String
Dictionary<string,List<string>>
EDIT 1 & 2:
I've thought of a better solution if you can use .NET 3.0 or higher.
Here's a LINQ example (I typed it without Visual Studio, so I hope it compiles ;)):
string[] lines = File.ReadAllLines("content.txt");
string[] separator = {":: "};
var splitOptions = StringSplitOptions.RemoveEmptyEntries;
var items = from line in lines
let parts = line.Split(separator, splitOptions)
group parts by parts[0] into partGroups
select partGroups;
A short explanation of the example above:
Get all lines from the file in a String array
Define some Split options (to keep the example readable)
For each line in the lines array, split it on the ":: "
Group the results of the split on the first split part (e.g. title, description, note, ...)
Store the grouped items in the items variable
The result of the LINQ query is a IQueryable<IGrouping<string, IEnumberable<string>>>.
Each item in the result has a Key property containing the key of the line (title, description, note, ...).
Each item can be enumerated containing all of values.
You could use a Lookup<TKey, TElement> :
ILookup<string, string> lookup = lines.Select(line => line.Split(new string[] { ":: " })
.ToLookup(arr => arr[0], arr => arr[1]);
IEnumerable<string> notes = lookup["note"];
Note that this collection is read-only
You may use PowerCollections which is an open source project that has a MultiDictionary data structure which solves your problem.
Here is a sample of how to use it.
Note: Jon Skeet suggested it before in his answer to this question.
I'm not a c# expert, but I think Dictionary<string, List<string>>
or some kind of HashMap<string, List<string>> might work.
For example (Java pseudocode):
aKey aValue
aKey anotherValue
if(map.get(aKey) == null)
{
map.put(aKey, new ArrayList(){{add(aValue);}});
}
else
{
map.put(aKey, map.get(aKey).add(anotherValue));
}
or something similar.
(or, the shortest way:
map.put(aKey, map.get(aKey) != null ? map.get(aKey).add(value) : new ArrayList(){{add(value);}});
I have used Dictionary<string, HashSet<string>> for getting multiple values in the past. I would love to know if there is something better though.
Here is how you can emulate getting only one value.
public static bool TryGetValue(this Dictionary<string, HashSet<string>> map, string key, out string result)
{
var set = default(HashSet<string>);
if (map.TryGetValue(key, out set))
{
result = set.FirstOrDefault();
return result == default(string);
}
result = default(string);
return false;
}