Say I have the following code:
Request.QueryString["ids"].Split('|');
If ids is not present in the query string this will throw an exception. Is there a generally accepted way to handle this type of situation. I think all of the following options would keep this from thorwing an error, but I'm wondering if one (or some different method entirely) is generally accepted as better.
string[] ids = (Request.QueryString["ids"] ?? "").Split('|');
or
string[] ids;
if(!String.IsNullOrEmpty(Request.QueryString["ids"]))
{
ids = Request.QueryString["ids"].Split('|')
}
or
?
I think all of these will work, but they look sort of ugly. Is there a better* way?
*better = easier to read, faster, more efficient or all of the above.
I like using an extension method for this:
public static string EmptyIfNull(this string self)
{
return self ?? "";
}
Usage:
string[] ids = Request.QueryString["ids"].EmptyIfNull().Split('|');
Personally I'd use
string idStr = Request.QueryString["ids"];
ids = idStr == null ? new string[0] : idStr.Split("|");
string[] ids = (Request.QueryString["ids"] as string).Split('|');
This will fail in the same manner as Request.QueryString["ids"]
string[] ids;
if(!String.IsNullOrEmpty(Request.QueryString["ids"]))
{
ids = Request.QueryString["ids"].Split('|')
}
Heavier and may call the data retrieval logic twice (and you might have side-effects done twice by error) => use a temporary to store the data but heavier.
string[] ids = (Request.QueryString["ids"] ?? "").Split('|');
Definetely the easiest, cleanest and more efficient way as the compiler will generate a temporary itself.
If you encounter this kind of processing a lot of time you can build your own plumbing library with a bunch of methods with fluent names and behaviors.
Related
I'm using a view returning Domains according to an id. The Domains column can be 'Geography' or can be stuffed domains 'Geography,History'. (In any way, the data returned is a VARCHAR)
In my C# code, I have a list containing main domains:
private static List<string> _mainDomains = new List<string>()
{
"Geography",
"Mathematics",
"English"
};
I want to filter my LINQ query in order to return only data related to one or many main Domain:
expression = i => _mainDomains.Any(s => i.Domains.Contains(s));
var results = (from v_lq in context.my_view
select v_lq).Where(expression)
The problem is I can't use the Any key word, nor the Exists keyword, since they aren't available in SQL. I've seen many solutions using the Contains keyword, but it doesn't fit to my problem.
What should I do?
You can use contains:
where i.Domains.Any(s => _mainDomains.Contains<string>(s.xxx))
Notice that the generic arguments are required (even if Resharper might tell you they are not). They are required to select Enumerable.Contains, not List.Contains. The latter one is not translatable (which I consider an error in the L2S product design).
(I might not have gotten the query exactly right for your data model. Please just adapt it to your needs).
I figured it out. Since I can't use the Any keyword, I used this function:
public static bool ContainsAny(this string databaseString, List<string> stringList)
{
if (databaseString == null)
{
return false;
}
foreach (string s in stringList)
{
if (databaseString.Contains(s))
{
return true;
}
}
return false;
}
So then I can use this expression in my Where clause:
expression = i => i.Domains.ContainsAny(_mainDomains);
Update:
According to usr, the query would return all the values and execute the where clause server side. A better solution would be to use a different approach (and not use stuffed/comma-separated values)
This is something I have always wondered about, and looked up a few times but have never figured out.
So basically what I want to do is get something to this effect:
List<string> strings = new List<string>(){"a","b","c"};
string aString = foreach(string s in strings){ if (s == "c") return s;}
so then after that, aString has the value "c".
I have tried using lambda expressions, maybe I just cant get them to work right, or maybe there is just no way to do this.
And obviously I want to do something a bit more complicated than in my example above, but it will work the same way.
Possible? not possible?
You should use the FirstOrDefault Extension method.
List<string> strings = new List<string>(){"a","b","c"};
return strings.FirstOrDefault(s=>String.Equals(s, "a"));
You can use LINQ (to objects):
List<string> strings = new List<string>(){"a","b","c"};
string aString = strings.Where(x => x.Equals("a")).FirstOrDefault();
The Where() methods iterates through the enumerable, and "returns" each element that satisfies the lambda. To get the first such element, you can chain on the FirstOrDefault() method (which will return default(string) if no elements meet the criteria.)
As #MichaelGraczyk points out, you can actually reduce the call to only FirstOrDefault(), since it has an overload that accepts a predicate:
string aString = strings.FirstOrDefault(x => x.Equals("a"));
There are a number of other useful methods available, which you can read about here.
It's kind of pointless in this particular example because you already know the string you want but in any case I think this is what you're trying to do...
List<string> strings = new List<string>(){"a","b","c"};
string aString = strings.Find((string s) => s == "a");
We're currently using LINQ to generate SQL queries, with a bit of magic inside to handle case-specific queries.
Up until now, it's worked fine; very fast, hardly any issues. We've recently run into efficiency issues when querying a large amount of data from the database.
We construct the query as such:
var someIntList = new List<int> { 1,2,3,4,5 };
var query = dtx.Query.Containers.Where(c => c.ContainerID.IsIn(someIntList));
or
var someStringList = new List<int> {"a", "b", "c" };
query = dtx.Query.Containers.Where(c => c.BuildingName.IsIn(someStringList));
Which would generate (along with a bunch of other stuff which isn't related to this):
SELECT * FROM Container WHERE ContainerID IN (1,2,3,4,5)
and
SELECT * FROM Container WHERE BuildingName IN ('a','b','c')
Now in this particular situation, we need to return 50,000 rows .. which is generated through 5 seperate queries, splitting up the load.
The DB returns fairly quickly (within seconds), however generating the query takes a long time.
Here's the very last function which is called to generate this particular query:
private static string GetSafeValueForItem(object item)
{
if (item == null)
return "NULL";
if (item is bool)
return ((bool)item ? "1" : "0");
if (item is string)
return string.Format("'{0}'", item.ToString().Replace("'", "''"));
if (item is IEnumerable)
return ListToDBList((IEnumerable)item);
if (item is DateTime)
return string.Format("'{0}'", ((DateTime)item).ToString("yyyy-MM-dd HH:mm:ss"));
return item.ToString();
}
private static string ListToDBList(IEnumerable list)
{
var str = list.Cast<object>().Aggregate("(", (current, item) => current + string.Format("{0},", GetSafeValueForItem(item)));
str = str.Trim(',');
str += ")";
return str;
}
Are there any obvious improvements which can be made to speed up the string concatenation in this case? Refactoring the code and using a different implementation (such as avoiding the query generating and hitting the database directly) is not preferred, but if it offered a big performance boost would be great to hear.
Your Aggregate code is basically string concatenation in a loop. Don't do that.
Options:
Use StringBuilder
Use string.Join
Here's an example using String.Join that outputs the same as your ListToDBList:
String.Format("({0})", String.Join(",", list.Cast<object>().Select(item=>GetSafeValueForItem(item)).ToArray()));
See here for an explanation why concatenating in a loop using + (which is what your call to Aggregate was doing) is slow: http://www.yoda.arachsys.com/csharp/stringbuilder.html
I haven't made test cases and profiled your code, so I don't know how much improvement you can expect.
Use a StringBuilder instead of String.Format and the += operator. The += operator is known to be slow. I suspect String.Format is going to be somewhat slow, too.
You could also try string.Join instead of manually joining the array. It works on IEnumerable in newer versions of the .NET framework (4.0?).
Not sure why you're doing list.Cast when a plain IEnumerable will be of objects anyway. But your whole ListToDBList can be replaced by
string.Format("({0})", string.Join(",",list.ToArray()));
Not sure how much quicker it would be, but it's clearer to my mind.
Somehow I can't seem to get string replacement within a foreach loop in C# to work. My code is as follows :
foreach (string s in names)
{
s.Replace("pdf", "txt");
}
Am still quite new to LINQ so pardon me if this sounds amateurish ;)
You say you're after a LINQ solution... that's easy:
var replacedNames = names.Select(x => x.Replace("pdf", "txt"));
We don't know the type of names, but if you want to assign back to it you could potentially use ToArray or ToList:
// If names is a List<T>
names = names.Select(x => x.Replace("pdf", "txt")).ToList();
// If names is an array
names = names.Select(x => x.Replace("pdf", "txt")).ToArray();
You should be aware that the code that you've posted isn't using LINQ at all at the moment though...
Strings in C# are immutable (does not change), so s.Replace will return a new string. Unfortunately this means you cannot use foreach to do the update. If names is an array this should work:
for(int i = 0; i < names.Length; i++)
{
names[i] = names[i].Replace("pdf", "txt");
}
As others have mentioned you'd need to use a for loop to do this in-place. However, if you don't need the operation to be done in-place (i.e. the results can be a different collection), then you could also do it as a linq query, e.g.
var results = from name in names select name.Replace("pdf", "txt");
One thing though - it looks like you are trying to change the extension of some file names. If that's what you are trying to do then I'd recommend Path.ChangeExtension which is specifically designed for this purpose.
var results = from name in names select Path.ChangeExtension(name, "txt");
s.Replace is a function so you would like s=s.Replace().. although it's better to use StringBuilder. (see upper answer)
Why use replace? It will make the application slow. Use regex instead:
http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex.replace.aspx
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;
}