I have a NameValueCollection in a usercontrol that is initialized like so:
private NameValueCollection _nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());
When I call the ToString() on this it generates a proper querystring which I can use for an updated url.
However, when I copy the NameValueCollection via its constructor like so:
var nameValues = new NameValueCollection(_nameValues);
And then try to form an url:
var newUrl = String.Concat(_rootPath + "?" + nameValues.ToString());
It outputs an url like this:
"http://www.domain.com?System.Collections.Specialized.NameValueCollection"
How can I copy a NameValueCollection so that the ToString() method outputs desired results?
The problem is there are two actual types in your code. The fist one is System.Web.HttpValueCollection which has it's ToString method overriden to get the result you expect and the second one is System.Collection.Specialized.NameValueCollection which does not override ToString. What you can do, if you really need to use System.Collection.Specialized.NameValueCollection is to create an extension method.
public static string ToQueryString(this NameValueCollection collection)
{
var array = (from key in collection.AllKeys
from value in collection.GetValues(key)
select string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))).ToArray();
return "?" + string.Join("&", array);
}
and use it:
var newUrl = String.Concat(_rootPath,nameValues.ToQueryString());
It is not NameValueCollection that provides the string formatting. That functionality is in an internal class System.Web.HttpValueCollection that is returned by HttpUtility.ParseQueryString.
So you will not be able to achieve this behavior by using built in functionality. Your best bet would be to create an extension method that formats the values in a URL format.
Here is the method from HttpValueCollection class - you might be able to use it with some modifications.
// System.Web.HttpValueCollection
internal virtual string ToString(bool urlencoded, IDictionary excludeKeys)
{
int count = this.Count;
if (count == 0)
{
return string.Empty;
}
StringBuilder stringBuilder = new StringBuilder();
bool flag = excludeKeys != null && excludeKeys["__VIEWSTATE"] != null;
for (int i = 0; i < count; i++)
{
string text = this.GetKey(i);
if ((!flag || text == null || !text.StartsWith("__VIEWSTATE", StringComparison.Ordinal)) && (excludeKeys == null || text == null || excludeKeys[text] == null))
{
if (urlencoded)
{
text = HttpValueCollection.UrlEncodeForToString(text);
}
string value = (text != null) ? (text + "=") : string.Empty;
string[] values = this.GetValues(i);
if (stringBuilder.Length > 0)
{
stringBuilder.Append('&');
}
if (values == null || values.Length == 0)
{
stringBuilder.Append(value);
}
else
{
if (values.Length == 1)
{
stringBuilder.Append(value);
string text2 = values[0];
if (urlencoded)
{
text2 = HttpValueCollection.UrlEncodeForToString(text2);
}
stringBuilder.Append(text2);
}
else
{
for (int j = 0; j < values.Length; j++)
{
if (j > 0)
{
stringBuilder.Append('&');
}
stringBuilder.Append(value);
string text2 = values[j];
if (urlencoded)
{
text2 = HttpValueCollection.UrlEncodeForToString(text2);
}
stringBuilder.Append(text2);
}
}
}
}
}
return stringBuilder.ToString();
}
internal static string UrlEncodeForToString(string input)
{
return HttpUtility.UrlEncodeUnicode(input);
}
Calling .ToString() on a name value collection will just give you the namespace it belongs to.
I suspect you want the key and value out of it, Assuming that it's the first in the collection why not just do:
var newUrl = String.Concat(_rootPath + "?" + nameValues.GetKey(0) + nameValues.Get(0));
You can have this as an extension method:
public static string ToString(this NameValueCollection nvc, int idx)
{
if(nvc == null)
throw new NullReferenceException();
string key = nvc[idx];
if(nvc.HasKeys() && !string.IsNullOrEmpty(key))
{
return string.Concat(key, nvc.Get(key)); //maybe want some formatting here
}
return string.Empty;
}
Usage:
NameValueCollection nvc = new NameValueCollection();
string foo = nvc.ToString(0); //gets key + value at index 0
Related
I would like to write C# code that parses nested parenthesis to array elements, but only on first level. An example is needed for sure:
I want this string:
"(example (to (parsing nested paren) but) (first lvl only))"
tp be parsed into:
["example", "(to (parsing nested paren) but)", "(first lvl only)"]
I was thinking about using regex but can't figure out how to properly use them without implementing this behaviour from scratch.
In the case of malformed inputs I would like to return an empty array, or an array ["error"]
I developed a parser for your example. I also checked some other examples which you can see in the code.
using System;
using System.Collections;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
string str = "(example (to (parsing nested paren) but) (first lvl only))"; // => [example , (to (parsing nested paren) but) , (first lvl only)]
//string str = "(first)(second)(third)"; // => [first , second , third]
//string str = "(first(second)third)"; // => [first , (second) , third]
//string str = "(first(second)(third)fourth)"; // => [first , (second) , (third) , fourth]
//string str = "(first((second)(third))fourth)"; // => [first , ((second)(third)) , fourth]
//string str = "just Text"; // => [ERROR]
//string str = "start with Text (first , second)"; // => [ERROR]
//string str = "(first , second) end with text"; // => [ERROR]
//string str = ""; // => [ERROR]
//string str = "("; // => [ERROR]
//string str = "(first()(second)(third))fourth)"; // => [ERROR]
//string str = "(((extra close pareanthese))))"; // => [ERROR]
var res = Parser.parse(str);
showRes(res);
}
static void showRes(ArrayList res)
{
var strings = res.ToArray();
var theString = string.Join(" , ", strings);
Console.WriteLine("[" + theString + "]");
}
}
public class Parser
{
static Dictionary<TokenType, TokenType> getRules()
{
var rules = new Dictionary<TokenType, TokenType>();
rules.Add(TokenType.OPEN_PARENTHESE, TokenType.START | TokenType.OPEN_PARENTHESE | TokenType.CLOSE_PARENTHESE | TokenType.SIMPLE_TEXT);
rules.Add(TokenType.CLOSE_PARENTHESE, TokenType.SIMPLE_TEXT | TokenType.CLOSE_PARENTHESE);
rules.Add(TokenType.SIMPLE_TEXT, TokenType.SIMPLE_TEXT | TokenType.CLOSE_PARENTHESE | TokenType.OPEN_PARENTHESE);
rules.Add(TokenType.END, TokenType.CLOSE_PARENTHESE);
return rules;
}
static bool isValid(Token prev, Token cur)
{
var rules = Parser.getRules();
return rules.ContainsKey(cur.type) && ((prev.type & rules[cur.type]) == prev.type);
}
public static ArrayList parse(string sourceText)
{
ArrayList result = new ArrayList();
int openParenthesesCount = 0;
Lexer lexer = new Lexer(sourceText);
Token prevToken = lexer.getStartToken();
Token currentToken = lexer.readNextToken();
string tmpText = "";
while (currentToken.type != TokenType.END)
{
if (currentToken.type == TokenType.OPEN_PARENTHESE)
{
openParenthesesCount++;
if (openParenthesesCount > 1)
{
tmpText += currentToken.token;
}
}
else if (currentToken.type == TokenType.CLOSE_PARENTHESE)
{
openParenthesesCount--;
if (openParenthesesCount < 0)
{
return Parser.Error();
}
if (openParenthesesCount > 0)
{
tmpText += currentToken.token;
}
}
else if (currentToken.type == TokenType.SIMPLE_TEXT)
{
tmpText += currentToken.token;
}
if (!Parser.isValid(prevToken, currentToken))
{
return Parser.Error();
}
if (openParenthesesCount == 1 && tmpText.Trim() != "")
{
result.Add(tmpText);
tmpText = "";
}
prevToken = currentToken;
currentToken = lexer.readNextToken();
}
if (openParenthesesCount != 0)
{
return Parser.Error();
}
if (!Parser.isValid(prevToken, currentToken))
{
return Parser.Error();
}
if (tmpText.Trim() != "")
{
result.Add(tmpText);
}
return result;
}
static ArrayList Error()
{
var er = new ArrayList();
er.Add("ERROR");
return er;
}
}
class Lexer
{
string _txt;
int _index;
public Lexer(string text)
{
this._index = 0;
this._txt = text;
}
public Token getStartToken()
{
return new Token(-1, TokenType.START, "");
}
public Token readNextToken()
{
if (this._index >= this._txt.Length)
{
return new Token(-1, TokenType.END, "");
}
Token t = null;
string txt = "";
if (this._txt[this._index] == '(')
{
txt = "(";
t = new Token(this._index, TokenType.OPEN_PARENTHESE, txt);
}
else if (this._txt[this._index] == ')')
{
txt = ")";
t = new Token(this._index, TokenType.CLOSE_PARENTHESE, txt);
}
else
{
txt = this._readText();
t = new Token(this._index, TokenType.SIMPLE_TEXT, txt);
}
this._index += txt.Length;
return t;
}
private string _readText()
{
string txt = "";
int i = this._index;
while (i < this._txt.Length && this._txt[i] != '(' && this._txt[i] != ')')
{
txt = txt + this._txt[i];
i++;
}
return txt;
}
}
class Token
{
public int position
{
get;
private set;
}
public TokenType type
{
get;
private set;
}
public string token
{
get;
private set;
}
public Token(int position, TokenType type, string token)
{
this.position = position;
this.type = type;
this.token = token;
}
}
[Flags]
enum TokenType
{
START = 1,
OPEN_PARENTHESE = 2,
SIMPLE_TEXT = 4,
CLOSE_PARENTHESE = 8,
END = 16
}
well, regex will do the job:
var text = #"(example (to (parsing nested paren) but) (first lvl only))";
var pattern = #"\(([\w\s]+) (\([\w\s]+ \([\w\s]+\) [\w\s]+\)) (\([\w\s]+\))\)*";
try
{
Regex r = new Regex(pattern, RegexOptions.IgnoreCase);
Match m = r.Match(text);
string group_1 = m.Groups[1].Value; //example
string group_2 = m.Groups[2].Value; //(to (parsing nested paren) but)
string group_3 = m.Groups[3].Value; //(first lvl only)
return new string[]{group_1,group_2,group_3};
}
catch(Exception ex){
return new string[]{"error"};
}
hopefully this helps, tested here in dotnetfiddle
Edit:
this might get you started into building the right expression according to whatever patterns you are falling into and maybe build a recursive function to parse the rest into the desired output :)
RegEx is not recursive. You either count bracket level, or recurse.
An non-recursive parser loop I tested for the example you show is..
string SplitFirstLevel(string s)
{
List<string> result = new List<string>();
int p = 0, level = 0;
for (int i = 0; i < s.Length; i++)
{
if (s[i] == '(')
{
level++;
if (level == 1) p = i + 1;
if (level == 2)
{
result.Add('"' + s.Substring(p, i - p) + '"');
p = i;
}
}
if (s[i] == ')')
if (--level == 0)
result.Add('"' + s.Substring(p, i - p) + '"');
}
return "[" + String.Join(",", result) + "]";
}
Note: after some more testing, I see your specification is unclear. How to delimit orphaned level 1 terms, that is terms without bracketing ?
For example, my parser translates
(example (to (parsing nested paren) but) (first lvl only))
to:
["example ","(to (parsing nested paren) but) ","(first lvl only)"]
and
(example (to (parsing nested paren)) but (first lvl only))
to:
["example ","(to (parsing nested paren)) but ","(first lvl only)"]
In either case, "example" gets a separate term, while "but" is grouped with the first term. In the first example this is logical, it is in the bracketing, but it may be unwanted behaviour in the second case, where "but" should be separated, like "example", which also has no bracketing (?)
I have a list of UserNames in a comma delimited string. I want to find next one of the input username.
For Example:
var s0 = "abc,deF,ghi,jkl";
var s1 = "abc";
var s2 = "def";
var s3 = "ghi";
var s4 = "jkl";
Result should be:
NextInString(s0,s1 ) == "def"
NextInString(s0,s2 ) == "ghi"
NextInString(s0,s3 ) == "jkl"
NextInString(s0,s4 ) == "jkl"
Here is what I have:
string NextInString(string listOfNames, string userName)
{
if(listOfNames == string.Empty || userName == string.Empty)
return string.Empty;
var s = listOfNames.Split(',');
var count = 0;
foreach (var element in s)
{
if (element == userName)break;
count++;
}
if (s.Length -1 == count)
{
return s[count];
}
else return s[ count + 1 ];
}
My question is, is there a better/easier way to approach this?
If you take the extra step to ensure your string list is trimmed, you can just use the IndexOf() method of List<T>:
string csv = "test1, test2, test3, test4";
List<string> names = csv.Split(',').Select(x => x.Trim()).ToList();
Then your NextInString() method (I think this is a poorly named method) would look like this:
private static string NextInString(List<string> names, string userName)
{
int index = names.IndexOf(userName);
if(names.Count - 1 == index || index == -1)
{
return "No result";
}
else
{
return names[index + 1];
}
}
I made a fiddle here
You can use Linq like this:
string userName = "abc";
string listOfNames = "abc,xyz,123";
var names = listOfNames
.Split(',')
.Select((n, i) => new {name = n, index =i} )
.ToArray();
var firstMatch = names.FirstOrDefault(n => n.name == userName);
var result = firstMatch == null
? string.Empty
: firstMatch.index == names.Length - 1
? string.Empty
: names[firstMatch.index + 1].name;
Here is the LINQ approach:
string NextInString(string listOfNames, string userName)
{
if(listOfNames == string.Empty || userName == string.Empty) return string.Empty;
var names = listOfNames.Split(',');
return names
.SkipWhile(x => x != userName)
.Skip(1)
.FirstOrDefault() ?? names.Last();
}
You can make a nice little extension method to do this after the string is split, like so:
static class IListExtensions
{
public static T FindItemAfter<T>(this IList<T> list, T targetItem)
{
return list[list.IndexOf(targetItem)+ 1];
}
}
You can use it like this:
static void Main(string[] args)
{
var list = "cat,dog,rat".Split(',');
Console.WriteLine(list.FindItemAfter("cat"));
Console.WriteLine(list.FindItemAfter("dog"));
Console.ReadLine();
}
It returns:
dog
rat
This overload will allow you to specify a default value that gets returned if the requested item isn't found, or the next item would be outside the list.
public static T FindItemAfter<T>(this IList<T> list, T targetItem, T defaultValue)
{
var index = list.IndexOf(targetItem);
if (index == -1 || index >= list.Count - 1)
{
return defaultValue;
}
return list[index + 1];
}
How about something like this?
var s = listOfNames.Split(',');
for (var i = 0; i < s.count; i++)
{
if (i == s.count - 1)
{
return string.Format("No user after {0} was found", userName);
}
else if (s[i] == userName)
{
return s[i + 1];
}
}
I want to compare a collection of strings and return the the equal parts until a not equal part occurs. (and remove traling whitespace).
example:
List<string> strList = new List<string>
{
"string xyz stop",
"string abc stop",
"string qrt stop"
};
string result = GetEqualName(strList); // This should return "string"
I made the following method that works
string GetEqualName(IEnumerable<string> strList)
{
string outString = "";
bool firstTime = true;
foreach (var subString in strList)
{
if (firstTime)
{
outString = subString;
firstTime = false;
}
else
{
string stringBuilder = "";
for (int i = 0; i < outString.Count(); i++)
{
if (outString[i] == subString[i])
stringBuilder = stringBuilder + outString[i];
else
break;
}
outString = stringBuilder;
}
}
outString = outString.TrimEnd(' '); // Remove traling whitespace
return outString;
}
I just feel that this is something that can be done in a few lines and I am overdoing it. Do you guys have any suggestions?
You can Zip two strings together, take the pairs that are equal, and then create a string of those characters.
public static string LargestCommonPrefix(string first, string second)
{
return new string(first.Zip(second, Tuple.Create)
.TakeWhile(pair => pair.Item1 == pair.Item2)
.Select(pair => pair.Item1)
.ToArray());
}
Once you've solved the problem for the case of combining two strings, you can easily apply it to a sequence of strings:
public static string LargestCommonPrefix(IEnumerable<string> strings)
{
return strings.Aggregate(LargestCommonPrefix);
}
This little function does basically the same as your version, but shorter.
string GetEqualName(IEnumerable<string> strList)
{
int limit = strList.Min(s => s.Length);
int i = 0;
for (; i < limit; i++)
{
if (strList.Select(s => s.Substring(0,i+1)).Distinct().Count() > 1)
{
break;
}
}
return strList.First().Substring(0, i).Trim();
}
Here's a different method which does what you want. I looks for the longest common substring from left to right using a HashSet<string>:
string GetCommonStartsWith(IEnumerable<string> strList, StringComparer comparer = null)
{
if(!strList.Any() || strList.Any(str => string.IsNullOrEmpty(str)))
return null;
if(!strList.Skip(1).Any())
return strList.First(); // only one
if(comparer == null) comparer = StringComparer.CurrentCulture;
int commonLength = strList.Min(str => str.Length);
for (int length = commonLength; length > 0; length--)
{
HashSet<string> duptester = new HashSet<string>(comparer);
string first = strList.First().Substring(0, length).TrimEnd();
duptester.Add(first);
bool allEqual = strList.Skip(1)
.All(str => !duptester.Add(str.Substring(0, length).TrimEnd()));
if (allEqual)
return first;
}
return null;
}
Here's a version that uses less LINQ than some of the other answers and might possibly be more performant.
string GetEqualName(IEnumerable<string> strList)
{
StringBuilder builder = new StringBuilder();
int minLength = strList.Min(s => s.Length);
for (int i = 0; i < minLength; i++)
{
char? c = null;
foreach (var s in strList)
{
if (c == null)
c = s[i];
else if (s[i] != c)
return builder.ToString().TrimEnd();
}
builder.Append(c);
}
return builder.ToString().TrimEnd();
}
I want remove "ids"one by one querystring from my url. How can i do this ? (using Asp.net4.0 , c#)
Default.aspx?ids=10,2,6,5
I want to remove"ids=6", but language would be the first,middle or last, so I will have this :
Default.aspx?ids=10,2,5,
Step 1. Have your ids in an array by:-
string[] idsarray = Request.QueryString["ids"].ToString().Split(',');
step 2. create a function to remove as per your language
string removeidat(string[] id, string at)
{
string toren = "";
int remat = -1;
if (at=="first")
{
remat = 0;
}
else if (at == "middle")
{
remat = id.Length / 2;
}
else
{
remat = id.GetUpperBound(0);
}
for (int i = 0; i < id.GetUpperBound(0); i++)
{
if (i!=remat)
{
toren += id[i] + ",";
}
}
if (toren.Length>0)
{
toren = toren.Substring(0, toren.Length - 1);
}
return toren;
}
Example : if you want to remove last id your code would be
string[] idsarray = Request.QueryString["ids"].ToString().Split(',');
string newids = removeidat(idsarray , "last")
string strIDs = Request.QueryString["ids"];
if(strIDs != null)
{
string[] ids = strIDs.Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries);
var no6 = ids.Where(id => id != "6");
string newUrl = string.Format("Default.aspx?ids={0}", string.Join(",", no6));
Response.Redirect(newUrl);
}
I need a little help. I am fairly new to reflection. We're using a 3rd party api and it returns a class called "AddressList". It has public properties within it literally called Address1, Address1Name, Address1Desc, Address2, Address2Name, Address2Desc, Address3, Address3Name, Address3Desc,... Address99, Address99Name, Address99Desc.. There are also a couple of other properties. I have a class called "SimpleAddress" that has just the 3 properties (Address, Name, Description). What I want to do is when I get the "AddressList" class returned, I would like to loop AddressDesc1... through AddressDesc99... and whichever ones are not null or empty, I would like to create an instance of "SimpleAddress", populate it's properties, and add it to a List... Can someone point me in the right direction? Obviously this would have been better if "AddressList" was some sort of collection, but unfortunately it is not. It is generated from a return string from a mainframe.
Thanks for any help,
~ck in San Diego
Ick. You could do something like this:
List<SimpleAddress> addresses = new List<SimpleAddress>();
string addressPropertyPattern = "Address{0}";
string namePropertyPattern = "Address{0}Name";
string descPropertyPattern = "Address{0}Desc";
for(int i = 1; i <= MAX_ADDRESS_NUMBER; i++)
{
System.Reflection.PropertyInfo addressProperty = typeof(AddressList).GetProperty(string.Format(addressPropertyPattern, i));
System.Reflection.PropertyInfo nameProperty = typeof(AddressList).GetProperty(string.Format(namePropertyPattern, i));
System.Reflection.PropertyInfo descProperty = typeof(AddressList).GetProperty(string.Format(descPropertyPattern, i));
SimpleAddress address = new SimpleAddress();
address.Address = (string)addressProperty.GetValue(yourAddressListObject, null);
address.Name = (string)nameProperty.GetValue(yourAddressListObject, null);
address.Description = (string)descProperty.GetValue(yourAddressListObject, null);
addresses.Add(address);
}
Start by getting the type of the class in question and invoke the GetProperties method.
PropertyInfo[] properties = myMainframeObject.GetType().GetProperties();
Each PropertyInfo has a Name attribute (a string) you can use to match against. Loop over all the properties, and write the code that creates a new instance of SimpleAddress.
Inside this loop, you can access your mainframe object and pull out the property values you need:
// imagine that in this case, 'p' is a PropertyInfo that represents Address2Name
var simpleAddress = new SimpleAddress();
simpleAddress.Name = p.GetValue(myMainframeObject, null);
(the null is never used for normal properties - it is intended for use with indexed properties).
You should be able to do something like:
List<SimpleAddress> CreateList(AddressList address)
{
List<SimpleAddress> values = new List<SimpleAddress>();
Type type = address.GetType();
for (int i=1;i<=99;++i)
{
string address = type.GetProperty("Address" + i.ToString()).GetValue(address,null).ToString();
string addressDesc = type.GetProperty("Address" + i.ToString() + "Desc").GetValue(address,null).ToString();
string addressName = type.GetProperty("Address" + i.ToString() + "Name").GetValue(address,null).ToString();
if (!string.IsNullOrEmpty(addressDesc) || !string.IsNullOrEmpty(addressName) || !string.IsNullOrEmpty(address) )
value.Add(new SimpleAddress(address,addressDesc,addressName));
}
return values;
}
Not tested (for obvious reasons), but something like:
List<SimpleAddress> newList = new List<SimpleAddress>();
AddressList list = ...
Type type = list.GetType();
PropertyInfo prop1, prop2, prop3;
int index = 1;
while((prop1 = type.GetProperty("Address" + index)) != null
&& (prop2 = type.GetProperty("Address" + index + "Name")) != null
&& (prop3 = type.GetProperty("Address" + index + "Desc")) != null) {
string addr = (string) prop1.GetValue(list, null),
name = (string) prop2.GetValue(list, null),
desc = (string) prop3.GetValue(list, null);
if(addr == null || name == null || desc == null) {
continue; // skip but continue
}
SimpleAddress newAddr = new SimpleAddress(addr, name, desc);
newList.Add(newAddr);
index++;
}
if you want to use linq
public static class MyTools
{
public static TReturn GetValue<TReturn>(this object input,
string propertyName)
{
if (input == null)
return default(TReturn);
var pi = input.GetType().GetProperty(propertyName);
if (pi == null)
return default(TReturn);
var val = pi.GetValue(input, null);
return (TReturn)(val == null ? default(TReturn) : val);
}
public static string GetString(this object input, string propertyName)
{
return input.GetValue<string>(propertyName);
}
public static List<SimpleAddress> GetAddress(this MyObject input)
{
return (
from i in Enumerable.Range(1, 2)
let address = input.GetString("Address" + i.ToString())
let name = input.GetString("Address" + i.ToString() + "Name")
let desc = input.GetString("Address" + i.ToString() + "Desc")
select new SimpleAddress() { Address = address,
Name = name,
Description = desc }
).ToList();
}
}
var addrList = new AddressList
{
Address1Name = "ABC",
Address1Desc = "DEF",
Address1 = "GHI",
Address3Name = "X",
Address3Desc = "Y",
Address3 = "Z"
};
var addresses =
from i in Enumerable.Range(1, 99)
let desc = typeof(AddressList).GetProperty(string.Format("Address{0}Desc", i)).GetValue(addrList, null) as string
let name = typeof(AddressList).GetProperty(string.Format("Address{0}Name", i)).GetValue(addrList, null) as string
let address = typeof(AddressList).GetProperty(string.Format("Address{0}", i)).GetValue(addrList, null) as string
where !string.IsNullOrEmpty(address)
select new SimpleAddress
{
Name = name,
Description = desc,
Address = address
};