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);
Related
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))
I'm somewhat new to C# and I'm trying to create a switch method that returns an ID (int) corresponding to the given filename.
For example:
var fileName = "file-example_MAP_COPY.xml";
var fileTypeId = GetFileTypeId(fileName); // Returns 3310
With the GetFileTypeId method looking something like this:
private GetFileTypeId(string fileName)
{
switch(string.Contains(fileName))
{
case ".xsd":
return 3010;
case "_Gui.xml":
return 3120;
case ".xml":
return 3300;
case "_MAP_COPY.xml":
return 3310;
...
}
}
I cannot trim the actual filename off and only keep the extension since the filename could contain underscores. A file with the name "example_1_MAP_COPY.xml" would be trimmed to "_1_MAP_COPY.xml" if trimmed at first underscore, resulting in a faulty file extension.
An if statement would work here, but since I have 18 different cases I'd like to find another solution than to write 18 if statements.
Is there some way I can go about to do this, either with a switch statement or a dictionary perhaps?
In current C#, you can do:
switch(filename) {
case string s when s.Contains(".xsd"): // or EndsWith, etc
...
}
I'm not saying that's the best approach or that it adds anything over if/else if, but... it works.
There's not much to choose between 18 complex case statements vs 18 if statements; except the if approach doesn't require you to add break; everywhere, and doesn't leak variable declarations between cases.
Personally, I'd use if/else if - or a static array of match/result pairs:
static readonly (string Match, int Result)[] MatchResults = new[] {
(".xsd", 3010),
("_Gui.xml", 3120),
// ...
};
...
foreach(var pair in MatchResults) {
if(filename.Contains(pair.Match)) return pair.Result;
}
You can use a switch indeed, but it seems you are only using the end of the string, so you could use another list type to save the patterns and their outcome:
var l = new []
{ new { Pattern = ".xsd", Value = 3010 }
, new { Pattern = "_MAP_COPY.xml", Value = 3310 }
};
foreach (var p in l)
{
if (filename.EndsWith(p.Pattern))
{
return p.Value;
}
}
// not found
You can use a switch indeed, but it seems you are only using the end of the string, so you could use a dictonary I think is simplier:
private readonly Dictionary<string, int> _ids = new Dictionary<string, int>
{
{".xsd", 3010},
{".xml", 3300},
{"_Gui.xml", 3120},
{"_MAP_COPY.xml", 3310}
};
private int GetFileTypeId(string fileName)
{
var element = _ids.FirstOrDefault(_ => fileName.Contains(_.Key));
return element.Value;
}
How can I put a string to the next empty index in a string array? I want to use a foreach loop and see if all the boolean strings are valid bools, and then put the keys of the invalid boolean strings to an array
string[] invalidKeys;
foreach (string key in ConfigurationManager.AppSettings)
{
string value = ConfigurationManager.AppSettings[key];
if (IsValidBooleanString(value) == false)
{
//Add 'key' to next empty index in the array 'invalidKeys'.
}
return invalidKeys;
You haven't initialized or specified the length of the array. You need to specify the length for the Array to create one, but in your case you don't know that info in advance
So you can use a List instead
var invalidKeys = new List<String>();
foreach (string key in ConfigurationManager.AppSettings.Keys)
{
string value = ConfigurationManager.AppSettings[key];
if (IsValidBooleanString(value) == false)
{
//Add 'key' to next empty index in the array 'invalidKeys'.
invalidKeys.Add(key);
}
return invalidKeys;
Also noticed your foreach should be on ConfigurationManager.AppSettings.Keys not ConfigurationManager.AppSettings
Other way of doing this will be
var invalidKeys =
ConfigurationManager.AppSettings.Keys
.Where(k => IsValidBooleanString(ConfigurationManager.AppSettings[k]) == false)
.ToArray();
If you really want to become a C# programmer:
return (from string key in ConfigurationManager.AppSettings
where !IsValidBooleanString(ConfigurationManager.AppSettings[key])
select key).ToList();
Or ToArray() or whatever.
The functionality of IsValidBooleanString() may be useful elsewhere. I would recommend placing it in an extension class:
public static class StringExtender
{
static readonly string[] validBooleanStrings = { "True", "False", "Yes", "No" };
public static bool IsValidBooleanString(this string value)
{
return ValidBooleanStrings.Contains(value, StringComparer.OrdinalIgnoreCase);
}
}
Some will say it's overkill to create an extension class for a single method, but it's really not and it makes your code modular and reusable. Not to mention you can add more extension methods here as you encounter similar scenarios.
Now all string objects have a .IsValidBooleanString() method attached to them and automatically pass themselves (this) to it. MSDN: Extension Methods (C#)
For retreiving a list of all setting keys with invalid boolean string values, I would use a LINQ query:
var settings = ConfigurationManager.AppSettings;
// Gets all the keys for values that are invalid boolean strings.
var invalidKeys = from key in settings.Keys
where !settings[key].IsValidBooleanString()
select key;
// If you want a list...
var invalidKeyList = invalidKeys.ToList<string>();
// If you want an array...
var invalidKeyArray = invalidKeys.ToArray<string>();
LINQ queries return IEnumerable<> values based on what you give it. Since I am selecting on a string type (key), it knows to give me back an IEnumerable<string>. You can then create a List<string> or string[] from the results if you wish.
Is there any way to compare two list of strings(regardless of case sensitivity) or do I need to write custom code for such comparison? I also want to remove non-matching items from my dictionary.
e.g
List<string> lst1 = new List<string>();
lst1.Add("value1");
lst1.Add("VALUE2");
List<string> lst2 = new List<string>();
lst2.Add("value1");
lst2.Add("value2");
lst2.Add("value3");
Now after comparison I want to have only "value1" and "value2" in lst2.
Regards,
JS
You can use LINQ Intersect method.
var result = lst1.Intersect(lst2, StringComparer.InvariantCultureIgnoreCase);
You can avoid creating your own implementation of IEqualityComparer<string> by using StringComparer
If you want the result to be in the lst2, then do it like that:
lst2 = lst1.Intersect(lst2, StringComparer.InvariantCultureIgnoreCase).ToList();
You can use the Enumerable.Intersect method
Refer to the MSDN documentation for examples: http://msdn.microsoft.com/en-us/library/bb460136.aspx
Refer to Dyppl's answer for implementing the Case insensitive comparison.
You can use the Intersect extension method. To do it case insenstive you can use an equaty comparer:
class Program
{
static void Main(string[] args)
{
List<string> lst1 = new List<string>();
List<string> lst2 = new List<string>();
CaseInsensitiveEquityComparer comparer = new CaseInsensitiveEquityComparer();
var result = lst1.Intersect(lst2, comparer);
}
}
class CaseInsensitiveEquityComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return String.Compare(x,y,true,CultureInfo.CurrentCulture) == 0;
}
public int GetHashCode(string obj)
{
return obj.GetHashCode();
}
}
Say I have a List of objects, and the object has a string property. I want to get a single comma-separated list of the value of each string property of each object in the list.
Here's 1 way to do it (sans linq)
StringBuilder result = new StringBuilder()
foreach(myObject obj in myList)
{
result.Append(obj.TheString);
result.Append(", ");
}
// then trim the trailing ", " and call ToString() on result, etc, etc...
Here's my first shot at linqification. Is there a better way?
string result = string.Join(", ", myList.Select(myObj => myObj.TheString).ToArray());
That's one line of code, but it doesn't look very efficient to me -- iterate the list just to build an array, just to iterate the array and build a string... whew!
Is there a better way?
If you want efficient, use Enumerable.Aggregate with StringBuilder:
string result = myList.Aggregate(new StringBuilder(),
(sb, o) => sb.Append(o.TheString).Append(", "))
.ToString();
The original problem is that String.Join wants an array. In .NET 4, there will be an overload that takes IEnumerable<string> (and I expect it will be implemented like above).
I like this extension method for joining strings. It's basically the same technique you are using, but wrapped in an extension method. I wouldn't recommend it for large sets since efficiency was not the goal. The benefit to me is expressiveness (very linqy) and convenient for small sets:
[Test]
public void Should_make_comma_delimited_list()
{
var colors = new List<HasColor>
{
new HasColor { Color = "red" },
new HasColor { Color = "green" },
new HasColor { Color = "blue" }
};
var result = colors.Implode(x => x.Color, ", ");
Assert.That(result, Is.EqualTo("red, green, blue"));
}
public class HasColor
{
public string Color { get; set; }
}
public static class LinqExtensions
{
public static string Implode<T>(this IEnumerable<T> list, Func<T, string> func, string separator)
{
return string.Join(separator, list.Select(func).ToArray());
}
}
Use string.Join, it's good enough.
Optimize when profiler will tell you to do so.
Readability of the version with StringBuilder is poor and you are still getting your trailing comma.
Here's another way (result is a StringBuilder):
myList.ForEach(
myObj =>
{
if (result.Length != 0)
result.Append(",");
result.Append(myObj.TheString);
}
);