I have this code:
bool validInput = !string.IsNullOrWhiteSpace(reg_name_tbx.Text)
&& !string.IsNullOrWhiteSpace(reg_adr_tbx.Text)
&& !string.IsNullOrWhiteSpace(reg_phn_tbx.Text)
&& !string.IsNullOrWhiteSpace(reg_pwr_tbx.Text)
&& !string.IsNullOrWhiteSpace(reg_email_tbx.Text)
&& !string.IsNullOrWhiteSpace(reg_type_cbx.Text);
Is there a better way to write this?
It is checking that all text boxes have valid input from the user..
As mentioned in the comments, you cannot make your code any more performant than it already is. There is no overload for string.IsNullOrWhiteSpace that takes multiple parameters and you will have to check each string individually.
That said, if you want to make the code more terse, you can encapsulate the check in a method that takes an array of strings:
public bool DoAllStringsHaveContent(params string[] input)
{
foreach (var item in input)
{
if (string.IsNullOrWhiteSpace(item))
return false;
}
return true;
}
You can then call it like so:
bool validInput = DoAllStringsHaveContent(reg_name_tbx.Text,
reg_adr_tbx.Text, reg_phn_tbx.Text, reg_pwr_tbx.Text,
reg_email_tbx.Text, reg_type_cbx.Text);
And for completeness' sake, if you want to do this in a "one-liner" without a reusable method, you can use LINQ:
bool validInput = new string[]
{
reg_name_tbx.Text, reg_adr_tbx.Text, reg_phn_tbx.Text,
reg_pwr_tbx.Text, reg_email_tbx.Text, reg_type_cbx.Text
}.All(x => !string.IsNullOrWhiteSpace(x);
If the textboxes are all on the form:
this.Controls.OfType<TextBox>().Any(tb => string.IsNullOrWhiteSpace(tb.Text))
If they're in a panel, replace this with the name of the panel
If you have other textboxes that shouldn't be included you can add some other condition that reduces the candidate textboxes to just those you're interested in; perhaps:
this.Controls.OfType<TextBox>().Any(tb => tb.Name.StartsWith("reg") && string.IsNullOrWhiteSpace(tb.Text))
Related
I have a method which is receiving the following input: params KeyValuePair<string, string>[] paramsToUse
In the method receiving the above input, is there a neat clean inline way of verifying that:
paramsToUse contains a KeyValuePair where the key is some specific
string, e.g. customerId, and ...
The value in that specific KeyValuePair (for which the above condition is true) is valid when verified through another static method that returns true/false, e.g. CheckIfCustomerIdIsValid(string customerIdToValidate)
One obvious way is to do a foreach loop over all the elements in paramsToUse and verify if a KeyValuePair exists which meets the above criteria, but I'm wondering if there's a cleaner way to do the same using LINQ perhaps ?
EDIT:
To make further clear, I want to see if there's a way to shorten the code below:
bool found = false;
foreach (KeyValuePair<string, string> pair in paramsToUse)
{
if (pair.Key == "customerId" && CheckIfCustomerIdIsValid(pair.Value) == true)
{
found = true;
break;
}
}
Output would be the value of found
Your code is fine. To make it lexically shorter, you can use LINQ:
bool found = paramsToUse.Any(pair => pair.Key == "customerId"
&& CheckIfCustomerIdIsValid(pair.Value));
This will basically execute the same code but hide the loop in the Any method.
I am exploring if there is a way to check if any of the values present in an array are present in a table without using a loop.
So even if 1 value is present in the table the method should return true, if none of the values are present then it should return the default false.
I am thinking like the below, below is not a working code just an idea.. so wanted to check if there is an efficient way to achieve this.
public bool CheckTable(string strList)
{
bool IsPresent = False;
String[] Arr = strList.split(',');
foreach(string str in Arr)
{
var x = DBContext.TableEmployees.where(e => e.Location == str).select(e => e.Location);
if(x == null)
{
isPresent = false;
}
else
{
isPresent = true;
}
}
}
Like this:
public bool CheckTable(string strList)
{
string[] strs = strList.Split(',');
return DBContext.TableEmployees.Any(e => strs.Contains(e.Location));
}
Take a read of https://www.microsoftpressstore.com/articles/article.aspx?p=2225065&seqNum=4 for more background on IN/EXISTS and how LINQ expressions are mapped to them.
When working with LINQ it's always wise to be mindful that if your expression can not be understood and translated completely into sql it might not run entirely at the server. Sometimes LINQ to sql will download data and rummage through it locally - the "store execution" section of https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/query-execution has some info (the whole document is worth reading, actually - deferred execution is a common gotcha too)
I have this code which i want to change:
foreach (DirectoryInfo path in currDirs) {
if (!newDirs.Contains(path)) {
MyLog.WriteToLog("Folder not Found: "+path.Name + "in New Folder. ",MyLog.Messages.Warning);
currNoPairs.Add(path);
}
}
In the If part i don't want to check the path i want to check the path.Name.
So how can i use the Contains method on the properties.
the goal is to sort out all folders that have not the same name in the list of Current Directory List and New Directory List.
See - IEnumerable<T>.Contains with predicate
Those functions that take "predicates" (boolean functions that signify a match) will let you do more complex checks. In this case, you can use them to compare sub-properties instead of the top-level objects.
The new code will look something like this:
foreach (DirectoryInfo path in currDirs) {
if (!newDirs.Any(newDir => newDir.Name == path.Name)) {
// TODO: print your error message here
currNoPairs.Add(path.Name);
}
}
In reply to your comment:
Okay i understood, but whats the diffrence between any and contains then?
List<T>.Contains
This method goes through each item in the list, seeing if that item is equal to the value you passed in.
The code for this method looks a little like this (simplified here for illustration):
for(var item in yourList) {
if(item.Equals(itemYouPassedIn) {
return true;
}
}
return false;
As you see, it can only compare top-level items. It doesn't check sub-properties, unless you are using a custom type that overrides the default Equals method. Since you're using the built in DirectoryInfo types, you can't override this Equals behavior without making a custom derived class. Since there's easier ways to do this, I wouldn't recommend this approach unless you need to do it in a ton of different places.
IEnumerable<T>.Any
This method goes through each item in the list, and then passes that item to the "predicate" function you passed in.
The code for this method looks a little like this (simplified for illustration):
for(var item in yourList) {
if(isAMatch(item)) { // Note that `isAMatch` is the function you pass in to `Any`
return true;
}
}
return false;
Your predicate function can be as complicated as you want it to be, but in this case, you'd just use it to check if the sub-properties are equal.
// This bit of code defines a function with no name (a "lambda" function).
// We call it a "predicate" because it returns a bool, and is used to find matches
newDir => newDir.Name == path.Name
// Here's how it might look like if it were defined as a normal function -
// this won't quite work in reality cause `path` is passed in by a different means,
// but hopefully it makes the lambda syntax slightly more clear
bool IsAMatch(DirectoryInfo newDir) {
return newDir.Name == path.Name;
}
Since you can customize this predicate every place that you use it, this could be a better tactic. I'd recommend this style until you are doing this exact check in a bunch of places in your code, in which case a custom class might be better.
Here is how you check for property Any
foreach (DirectoryInfo path in currDirs) {
if (!newDirs.Any(dir => dir.FullName == path.FullName)) {
MyLog.WriteToLog("Folder not Found: "+path.Name + "in New Folder. ",MyLog.Messages.Warning);
currNoPairs.Add(path);
}
}
And by the way, your code could be written in a better way like this
var currDirsConcrete = currDirs.ToArray();
var pathsNotFound = "Following paths were not found \r\n " + string.Join("\r\n", currDirsConcrete.Where(d => d.FullName != path.FullName).ToArray());
var pathsFound = currDirsConcrete.Where(d => d.FullName == path.FullName).ToArray();
MyLog.WriteToLog(pathsNotFound, MyLog.Messages.Warning);
Note: You can skip the first line currDirsConcrete if your currDirs is already an array or a list. I did this to avoid redetermining the enumerable.
I would use linq with except and implement a DirComparator
List<DirectoryInfo> resultExcept = currDirs.Except(newDirs, new DirComparator()).ToList();
Here the IEqualityComparer<DirectoryInfo>:
public class DirComparator : IEqualityComparer<DirectoryInfo> {
public bool Equals(DirectoryInfo x, DirectoryInfo y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the products' properties are equal.
return x.Name.equals(y.Name);
}
public int GetHashCode(DirectoryInfo dir)
{
//Check whether the object is null
if (Object.ReferenceEquals(dir, null)) return 0;
//Get hash code for the Name field if it is not null.
return dir.Name == null ? 0 : dir.Name.GetHashCode();
}
}
you could also use intersect if you want it the other way around.
Is there a way to compare two strings to another string at once?
Example:
string1 == string2 && string3;
(I know this isn't the way, just a representation of what I mean)
Generally, no, there is no way to do this that resembles the way you asked to do it.
However, if the strings to compare with are in a collection of some kind, you can do this:
if (stringCollection.All(s => s == string1) )
If you don't want to deal with putting your values into a collection or list, you could do an extension method like this:
static class extensions
{
private static bool InSet(this string s, params string[] p )
{
return p.Contains(s);
}
}
Then when you want to test if a string matches a value in a set you can do this:
if (String1.InSet(String2, String3))
{
// do something.
}
Besides Linq's All method, you can do also with List methods, like TrueForAll
string searchString = "hi";
var listStrings = new List<string>() { "hi", "there" };
bool allOfThem = listStrings.TrueForAll(x => x == searchString);
bool atLeastOne = listStrings.Contains(searchString);
How would you refactor this method?
private bool IsTermExist(string word)
{
var query = from term in m_xdoc.Descendants("Term")
where term.Attribute("Name").Value.ToUpper().Contains(word.ToUpper())
select term;
return query.Any();
}
I would probably use the overload of Any which accepts a predicate:
private bool TermExists(string word)
{
return m_xdoc.Descendants("Term")
.Any(term => term.Attribute("Name").Value
.IndexOf(word, StringComparison.OrdinalIgnoreCase) >= 0);
}
Notes:
Using ToUpper for case-insensitivity is generally fraught (e.g. due to behaviour in Turkish which is unexpected to most developers); using the overload of IndexOf which takes a StringComparison is preferred. (There's a similar overload for Equals.)
If we were going to compare with word.ToUpper(), we could extract it so that word.ToUpper() is only executed once, instead of once per Term element. (Micro-optimization aside, I think it's generally a nice idea to extract the iteration-invariant parts of a query first. But don't expect consistency on this front :)
This code will still go bang for Term elements without Name attributes. That may or may not be what you want.
If you want to return the first matching item instead how about a method that either returns an item or null if not found:
private XElement? GetTerm(string word)
{
var query = from term in m_xdoc.Descendants("Term")
where term.Attribute("Name").Value.ToUpper().Contains(word.ToUpper())
select term;
return query.FirstOrDefault();
}
I'd probably write this:
private static bool MatchesName(XElement el, string name)
{
var attr = el.Attribute("Name");
return attr.Value.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0;
}
private bool TermExists(string word)
{
return m_xdoc.Descendants("Term").Any(e => MatchesName(e, word));
}
Hard to say where to split off the helper method without seeing other code, though.