So I have a
if (toolStripTextBox1.Text.Contains("text1", "text2", "text3", ... "text139"
and then I started adding 139 items until I realized it didn't work
how would I overload the Contains method? Or just bypass the limit..
Use LINQ:
var items = new[]{"text1", "text2", "text3", ... "text139"};
if (items.Any(item => toolStripTextBox1.Text.Contains(item)) ...
Well, it's simply an AND operation. What you really want to achieve is to know whether a text simultaneously contains text1 AND text2 AND text3 and so forth. So simply you want str.Contains("text1") && str.Contains("text2").
But pretty much it seems like you are doing some code smell. However, we are not aware of the full scenario. It will be better to add all the parts in a collection, loop through it and check if the string contains each of the values.
If you want to check if your text box contains at least one word, you can stop on the first occurrence:
string[] words = { "text1", "text2", "text3" };
foreach (var word in words)
{
if (toolStripTextBox1.Text.Contains(word)) break;
}
If you want to check if your text box contains all the words, you need to stop on the first word which is not contained and flag that something's wrong:
string[] words = { "text1", "text2", "text3" };
bool allFound = true;
foreach (var word in words)
{
if (!toolStripTextBox1.Text.Contains(word))
{
allFound = false;
break;
}
}
if (allFound) { // }
This is exactly how Any() and All() extension methods from System.Linq work. So you can use them instead (less code)
string[] words = { "text1", "text2", "text3" };
if (words.All(w => toolStripTextBox1.Text.Contains(w))
{
// do smth if all words are contained
}
Or
string[] words = { "text1", "text2", "text3" };
if (words.Any(w => toolStripTextBox1.Text.Contains(w))
{
// do smth if at least one word is contained
}
You can build your own extension method of it some thing like:
public enum Operation
{
And,
Or
}
public static bool Contains(this string text,Operation operation,params string[] args)
{
switch(operation)
{
case Operation.And:
return args.All(item => text.Contains(item));
case Operation.Or:
return args.Any(item => text.Contains(item));
default:
return false;
}
}
and use it like
if(toolStripTextBox1.Text.Contains(Operation.And,"text1", "text2", "text3"))
OR
if(toolStripTextBox1.Text.Contains(Operation.Or,"text1", "text2", "text3"))
Related
C# Folks! I have 2 List that I want to compare.
Example:
List<string> ONE contains:
A
B
C
List<string> TWO contains:
B
C
I know I can achieve the results of ONE if I do:
ONE.Except(TWO);
Results: A
How can I do the same if my Lists contain a file extension for each
Element?
List<string> ONE contains:
A.pdf
B.pdf
C.pdf
List<string> TWO contains: (will always have .txt extension)
B.txt
C.txt
Results should = A.pdf
I realized that I need to display the full filename (A.pdf) in a report at the end, so I cannot strip the extension, like I originally did.
Thanks for the help!
EDIT:
This is how I went about it, but I am not sure if this is the "best" or "most performant" way to actually solve it, but it does seem to work...
foreach (string s in ONE)
{
//since I know TWO will always be .txt
string temp = Path.GetFileNameWithoutExtension(s) + ".txt";
if (TWO.Contains(temp))
{
// yes it exists, do something
}
else
{
// no it does not exist, do something
}
}
This a very straightforward and a easy code , but if your requirement has more file extension
List<string> lstA = new List<string>() { "A.pdf", "B.pdf", "C.pdf" };
List<string> lstB = new List<string>() { "B.txt", "C.txt" };
foreach (var item in lstA)
{
if (lstB.Contains(item.Replace(".pdf",".txt"))==false)
{
Console.WriteLine(item);
}
}
You can implement a custom equality comparer:
class FileNameComparer: IEqualityComparer<String>
{
public bool Equals(String b1, String b2)
{
return Path.GetFileNameWithoutExtension(b1).Equals(Path.GetFileNameWithoutExtension(b2));
}
public int GetHashCode(String a)
{
return Path.GetFileNameWithoutExtension(a).GetHashCode();
}
}
... and pass it to the Except method:
System.Console.WriteLine(string.Join(", ", list1.Except(list2, new FileNameComparer())));
I have a list of strings, which can be considered 'filters'.
For example:
List<string> filters = new List<string>();
filters.Add("Apple");
filters.Add("Orange");
filters.Add("Banana");
I have another list of strings, which contains sentences.
Example:
List<string> msgList = new List<string>();
msgList.Add("This sentence contains the word Apple.");
msgList.Add("This doesn't contain any fruits.");
msgList.Add("This does. It's a banana.");
Now I want to find out which items in msgList contains a fruit. For which, I use the following code:
foreach(string msg in msgList)
{
if(filters.Any(msg.Contains))
{
// Do something.
}
}
I'm wondering, is there a way in Linq where I can use something similar to List.Any() where I can check if msgList contains a fruit, and if it does, also get the fruit which matched the inquiry. If I can get the matching index in 'filters' that should be fine. That is, for the first iteration of the loop it should return 0 (index of 'Apple'), for the second iteration return null or something like a negative value, for the third iteration it should return 2 (index of 'Banana').
I checked around in SO as well as Google but couldn't find exactly what I'm looking for.
You want FirstOrDefault instead of Any.
FirstOrDefault will return the first object that matches, if found, or the default value (usually null) if not found.
You could use the List<T>.Find method:
foreach (string msg in msgList)
{
var fruit = filters.Find(msg.Contains);
if (fruit != null)
{
// Do something.
}
}
List<string> filters = new List<string>() { "Apple", "Orange", "Banana" };
string msg = "This sentence contains the word Apple.";
var fruit = Regex.Matches(msg, #"\w+", RegexOptions.IgnoreCase)
.Cast<Match>()
.Select(x=>x.Value)
.FirstOrDefault(s => filters.Contains(s));
A possible approach to return the indexes of the elements
foreach (string msg in msgList)
{
var found = filters.Select((x, i) => new {Key = x, Idx = i})
.FirstOrDefault(x => msg.Contains(x.Key));
Console.WriteLine(found?.Idx);
}
Note also that Contains is case sensitive, so the banana string is not matched against the Banana one. If you want a case insensitive you could use IndexOf with the StringComparison operator
The following code could be used to search in an array / List of strings using LINQ.
String[] someArray
= { "Test1", "test2", "test3", "TEST4" };
string toCheck = "Test1";
if (someArray.Any(toCheck.Contains))
{
// found -> Do sth.
}
// or with list
List<string> someList
= new List<string>(new string[] { "TEST1", "test2", "test3", "TEST4" });
if (someList.Any(toCheck.Contains))
{
// "Test1" != "TEST1"
}
But how could you do this case invariant?
My approach was to convert the complete list to upper, and then test using contains:
if ((someList.ConvertAll(item => item.ToUpper()).Any(toCheck.ToUpper().Contains)))
{
// found -> Do sth.
}
In this case the original list is not altered.
if ((someList.Select(item => item.ToUpper()).Any(toCheck.ToUpper().Contains)))
{
// works with both
}
Well it works... (also with some language specific things like the turkish 'i' letter... (also we still don't have turkish customers as far as i know.. but who knows if they're are in future?)), but it seems not very elegant.
Is there a way to do an case invariant comparision if an item is in a list?
Best regards,
Offler
Instead of Contains use IndexOf with StringComparison.OrdinalIgnoreCase:
String[] strings = { "Test1", "test2", "test3", "TEST4" };
String text = "TEST123";
if (strings.Any(str => text.IndexOf(str, StringComparison.OrdinalIgnoreCase) > -1))
{
// we will enter this if clause
}
Demo
Could you not just do a simple check with the IndexOf that uses the appropriate StringComparison value? For example:
if(someArray.Any(s => s.IndexOf(toCheck, StringComparison.CurrentCultureIgnoreCase) != -1)
{
// do something
}
So I have been asked to remove tour codes that end with the letter G, GE, G=, or Z. The only bad thing is I believe we use this call for a lot of pages and that is the reason I cant alter the database call in the first place so I want to do this specifically for this one person. My code is calling an arrayList that fills with all the tourcodes we have. Is there any way I can remove the tours with the letters above. Here is what I got to work with.
public void LoadTourCodes()
{
ddlTourCode.Items.Clear();
if (ddlTourCode.Visible)
{
ddlTourCode.Items.Add(new ListItem(" ", ""));
ArrayList tourCodes;
tourCodes = EblTipTours.FindTourCodes();
foreach (string tourCode in tourCodes)
{
ddlTourCode.Items.Add(tourCode);
}
}
}
You can do it using LINQ, like this:
var toRemove = new []{"G", "GE", "G=", "Z"};
foreach (string tourCode in tourCodes.Where(code => !toRemove.Any(suffix => code.EndsWith(suffix)))) {
ddlTourCode.Items.Add(tourCode);
}
If you cannot use LINQ because it's a legacy system, you can rewrite the code like this:
string[] toRemove = new string[] {"G", "GE", "G=", "Z"};
foreach (string tourCode in tourCodes) {
bool good = true;
foreach (string suffix in toRemove) {
if (tourCode.EndsWith(suffix)) {
good = false;
break;
}
}
if (!good) continue;
ddlTourCode.Items.Add(tourCode);
}
I'm doing some validation where I need to check for certain combinations between two values. For example, if string1 is "fruit", valid values for string2 are "apple", "banana" and "pear". Currently, I'm doing this:
switch(string1)
{
case "fruit":
if(string2 != "apple" && string2 != "banana")
{
return false;
}
break;
case "meat":
if(string2 != "beef" && string2 != "pork")
{
return false;
}
default:
return true;
break;
}
This is really two questions. The first is, is there any good way to do something more like this:
switch(string1)
{
case "fruit":
if(string2 NOT IN ("apple", "banana"))
{
return true;
}
break;
case "meat":
if(string2 NOT IN ("beef", "pork"))
{
return false;
}
default:
return true;
break;
}
The second part of this question is likely what will get answered first: is there a better/best way to do this? I'm not the most amazing coder in the world and this is the first "off the top of my head" solution, so I'm certainly open to better ones. Thanks!
A variation on Nick's answer. Create two lists and use the contains method against them.
public List<string> Fruit = new List<string>{"apple", "banana"};
public List<string> Meat = new List<string>{"beef", "pork"};
switch (string1)
{
case "fruit":
return Fruit.Contains(string2);
case "meat":
return Meat.Contains(string2);
}
Yeah, there's a better way. You want to create a map, which associates your "category" ("fruit") with a string List of your elements ("apple", "banana", etc.). Then you want to look up your "string1" in your example above from the map and see if your associated string List Contains() your "string2".
This makes it entirely data-driven, and leverages the built-in abilities of the Collections more successfully.
Here is a way using Linq:
Dictionary<string, IList<string>> validValues = new Dictionary<string, IList<string>>()
{
{ "fruit", new List<string>() { "apple", "banana" } },
{ "meat", new List<string>() { "pork", "beef" } }
};
if (validValues.FirstOrDefault(x => x.Key == string1 && x.Value.Contains(string2)).Value != null)
{
return true;
}
return false;
You can shorten your cases down to:
switch(string1)
{
case "fruit":
return new[] { "apple", "banana" }.Contains(string2);
case "meat":
return new[] { "beef", "pork" }.Contains(string2);
default:
return true;
break;
}
I think style is, to a large degree, dependent upon personal taste. This is my taste...I think it's easy to add a new value in there with the array style, with as little overhead (I believe, someone feel free to correct me) that you can get with an array/collection style of "if-in" type check.
You could do something like this:
Dictionary<string, string> map = new Dictionary<string, string>();
map.add("banana", "fruit");
map.add("apple", "fruit");
map.add("pear", "fruit");
map.add("beef", "meat");
map.add("pork", "meat");
if(map[string2] == string1)
return true;