How to get if item exist in IEnumerable using Linq - c#

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.

Related

Check if list contains element that contains a string and get that element

While searching for an answer to this question, I've run into similar ones utilizing LINQ but I haven't been able to fully understand them (and thus, implement them), as I'm not familiarized with it. What I would like to, basically, is this:
Check if any element of a list contains a specific string.
If it does, get that element.
I honestly don't know how I would go about doing that. What I can come up with is this (not working, of course):
if (myList.Contains(myString))
string element = myList.ElementAt(myList.IndexOf(myString));
I know WHY it does not work:
myList.Contains() does not return true, since it will check for if a whole element of the list matches the string I specified.
myList.IndexOf() will not find an occurrence, since, as it is the case again, it will check for an element matching the string.
Still, I have no clue how to solve this problem, but I figure I'll have to use LINQ as suggested in similar questions to mine. That being said, if that's the case here, I'd like for the answerer to explain to me the use of LINQ in their example (as I said, I haven't bothered with it in my time with C#). Thank you in advance guys (and gals?).
EDIT: I have come up with a solution; just loop through the list, check if current element contains the string and then set a string equal to the current element. I'm wondering, though, is there a more efficient way than this?
string myString = "bla";
string element = "";
for (int i = 0; i < myList.Count; i++)
{
if (myList[i].Contains(myString))
element = myList[i];
}
You should be able to use Linq here:
var matchingvalues = myList
.Where(stringToCheck => stringToCheck.Contains(myString));
If you simply wish to return the first matching item:
var match = myList
.FirstOrDefault(stringToCheck => stringToCheck.Contains(myString));
if(match != null)
//Do stuff
The basic answer is: you need to iterate through loop and check any element contains the specified string.
So, let's say the code is:
foreach(string item in myList)
{
if(item.Contains(myString))
return item;
}
The equivalent, but terse, code is:
mylist.Where(x => x.Contains(myString)).FirstOrDefault();
Here, x is a parameter that acts like "item" in the above code.
string result = myList.FirstOrDefault(x => x == myString)
if(result != null)
{
//found
}
for (int i = 0; i < myList.Length; i++)
{
if (myList[i].Contains(myString)) // (you use the word "contains". either equals or indexof might be appropriate)
{
return i;
}
}
Old fashion loops are almost always the fastest.
If you want a list of strings containing your string:
var newList = myList.Where(x => x.Contains(myString)).ToList();
Another option is to use Linq FirstOrDefault
var element = myList.Where(x => x.Contains(myString)).FirstOrDefault();
Keep in mind that Contains method is case sensitive.
You could use Linq's FirstOrDefault extension method:
string element = myList.FirstOrDefault(s => s.Contains(myString));
This will return the fist element that contains the substring myString, or null if no such element is found.
If all you need is the index, use the List<T> class's FindIndex method:
int index = myList.FindIndex(s => s.Contains(myString));
This will return the the index of fist element that contains the substring myString, or -1 if no such element is found.
Many good answers here, but I use a simple one using Exists, as below:
foreach (var setting in FullList)
{
if(cleanList.Exists(x => x.ProcedureName == setting.ProcedureName))
setting.IsActive = true; // do you business logic here
else
setting.IsActive = false;
updateList.Add(setting);
}
You should be able to use something like this, it has worked okay for me:
var valuesToMatch = yourList.Where(stringCheck => stringCheck.Contains(myString));
or something like this, if you need to look where it doesn't match.
var valuesToMatch = yourList.Where(stringCheck => !stringCheck.Contains(myString));
you can use
var match=myList.Where(item=>item.Contains("Required String"));
foreach(var i in match)
{
//do something with the matched items
}
LINQ provides you with capabilities to "query" any collection of data. You can use syntax like a database query (select, where, etc) on a collection (here the collection (list) of strings).
so you are doing like "get me items from the list Where it satisfies a given condition"
inside the Where you are using a "lambda expression"
to tell briefly lambda expression is something like (input parameter => return value)
so for a parameter "item", it returns "item.Contains("required string")" . So it returns true if the item contains the string and thereby it gets selected from the list since it satisfied the condition.
To keep it simple use this;
foreach(string item in myList)//Iterate through each item.
{
if(item.Contains("Search Term")//True if the item contains search pattern.
{
return item;//Return the matched item.
}
}
Alternatively,to do this with for loop,use this;
for (int iterator = 0; iterator < myList.Count; iterator++)
{
if (myList[iterator].Contains("String Pattern"))
{
return myList[iterator];
}
}
It is possible to combine Any, Where, First and FirstOrDefault; or just place the predicate in any of those methods depending on what is needed.
You should probably avoid using First unless you want to have an exception thrown when no match is found. FirstOrDefault is usually the better option as long as you know it will return the type's default if no match is found (string's default is null, int is 0, bool is false, etc).
using System.Collections.Generic;
using System.Linq;
bool exists;
string firstMatch;
IEnumerable<string> matchingList;
var myList = new List<string>() { "foo", "bar", "foobar" };
exists = myList.Any(x => x.Contains("o"));
// exists => true
firstMatch = myList.FirstOrDefault(x => x.Contains("o"));
firstMatch = myList.First(x => x.Contains("o"));
// firstMatch => "foo"
firstMatch = myList.First(x => x.Contains("dark side"));
// throws exception because no element contains "dark side"
firstMatch = myList.FirstOrDefault(x => x.Contains("dark side"));
// firstMatch => null
matchingList = myList.Where(x => x.Contains("o"));
// matchingList => { "foo", "foobar" }
Test this code # https://rextester.com/TXDL57489
I have not seen bool option in other answers so I hope below code will help someone.
Just use Any()
string myString = "test";
bool exists = myList
.Where(w => w.COLUMN_TO_CHECK.Contains(myString)).Any();
You can check the list is empty or not in multiple ways.
1)Check list is null and then check count is greater than zero like below:-
if(myList!=null && myList.Count>0)
{
//List has more than one record.
}
2)Check list null and count greater than zero using linq query like below:-
if(myList!=null && myList.Count>0)
{
//List has more than one record.
}

Filtering a string list with logic?

I have a list of strings. I need to be able to filter them in a similar way to a Google query.
Ex: NOT water OR (ice AND "fruit juice")
Meaning return strings that do not have the word water or return strings that can have water if they have ice and "fruit juice".
Is there a mechanism in .NET that can allow the user to write queries in this form (say in a textbox) and given a List or IEnumerable of string, return the ones that contain this.
Can LINQ maybe do something like this?
I am aware that I can do this with LINQ, I'm more concerned with parsing an arbitrary string into an executable expression.
There is nothing built in.
You will need to parse such a string yourself and possibly use the Expression classes to build up an executable expression from which to filter.
For this query: Meaning return strings that do not have the word water or return strings that can have water if they have ice and "fruit juice".
Try something like this if you are going to use LINQ
yourList.Where(i => !i.Contains("water") ||
(i.Contains("water") &&
i.Contains("ice") &&
i.Contains("fruit juice")));
I think we can't answer you using a code sample since as you said this is more logical.
what I would do in such a scenario is to have predefined conditions (saved somewhere) which will contain all the conditions you need along with their "coding translations" like:
and
or
and not
and or
etc...
and what you will do at runtime is to translate these conditions/criteria into sql or linq or whatever language you need to pass it to.
In LINQ: list.Where(item => !item.Contains("water") || (item.Contains("ice") && item.Contains("fruit juice")))
You can try to use the DataTable.Select method:
public static class ExpressionExtensions {
public static IEnumerable<T> Select<T>(this IEnumerable<T> self, string expression) {
var table = new DataTable();
table.Columns.Add("Value", typeof(T));
foreach (var item in self) {
var row = table.NewRow();
row["Value"] = item;
table.Rows.Add(item);
}
return table.Select(expression).Select(row => (T)row["Value"]);
}
}
But you have to follow its format to create your expression:
var filtered = strings.Select("NOT Value LIKE '*water*' OR (Value LIKE '*ice*' AND Value LIKE '*fruit juice*')");
Also note that in this case, since the string fruit juice already contains the string ice, the second condition is redundant. You'd have to find a way to express "words" and not "substrings".
In the end, you might be better off implementing the parsing logic yourself.

Is this achievable with a single LINQ query?

Suppose I have a given object of type IEnumerable<string> which is the return value of method SomeMethod(), and which contains no repeated elements. I would like to be able to "zip" the following lines in a single LINQ query:
IEnumerable<string> someList = SomeMethod();
if (someList.Contains(givenString))
{
return (someList.Where(givenString));
}
else
{
return (someList);
}
Edit: I mistakenly used Single instead of First. Corrected now.
I know I can "zip" this by using the ternary operator, but that's just not the point. I would just list to be able to achieve this with a single line. Is that possible?
This will return items with given string or all items if given is not present in the list:
someList.Where(i => i == givenString || !someList.Contains(givenString))
The nature of your desired output requires that you either make two requests for the data, like you are now, or buffer the non-matches to return if no matches are found. The later would be especially useful in cases where actually getting the data is a relatively expensive call (eg: database query or WCF service). The buffering method would look like this:
static IEnumerable<T> AllIfNone<T>(this IEnumerable<T> source,
Func<T, bool> predicate)
{
//argument checking ignored for sample purposes
var buffer = new List<T>();
bool foundFirst = false;
foreach (var item in source)
{
if (predicate(item))
{
foundFirst = true;
yield return item;
}
else if (!foundFirst)
{
buffer.Add(item);
}
}
if (!foundFirst)
{
foreach (var item in buffer)
{
yield return item;
}
}
}
The laziness of this method is either that of Where or ToList depending on if the collection contains a match or not. If it does, you should get execution similar to Where. If not, you will get roughly the execution of calling ToList (with the overhead of all the failed filter checks) and iterating the result.
What is wrong with the ternary operator?
someList.Any(s => s == givenString) ? someList.Where(s => s == givenString) : someList;
It would be better to do the Where followed by the Any but I can't think of how to one-line that.
var reducedEnumerable = someList.Where(s => s == givenString);
return reducedEnumerable.Any() ? reducedEnumerable : someList;
It is not possible to change the return type on the method, which is what you're asking. The first condition returns a string and the second condition returns a collection of strings.
Just return the IEnumerable<string> collection, and call Single on the return value like this:
string test = ReturnCollectionOfStrings().Single(x => x == "test");

Contains functionality in Linq to XML Where Clause

I'm having unexpected behavior with the .Contains() function of the where clause in Linq to XML. It seems to be functioning like "==" not Contains() in the string function.
Example:
var q = from sr in SearchResults.Descendants("Result")
where _filters.Contains((string)sr.Element("itemtype"))
orderby (string)sr.Element("ipitemtype") ascending
select new SearchItem
{
//Create Object
ID = (string)sr.Element("blabla"),
}
_filters is a list of strings. Let's say it contains 3 values:
_filters[0] = "videos";
_filters[1] = "documents";
_filters[2] = "cat pictures";
What happens now, is that the Query works perfectly if
<itemtype>videos</itemtype>
is the XML node.
However, if the node is
<itemtype>videos mission critical document advertising</itemtype>,
the IEnumerable returns blank, which to me says the operand is functioning like "==" not "Contains()".
Any idea what I'm doing wrong?
Winning answer from dtb:
replace
where _filters.Contains((string)sr.Element("itemtype"))
with
where _filters.Any(filter => ((string)sr.Element("itemtype")).Contains(filter))
Try this:
_filters.Any(s => ((string)sr.Element("itemtype") ?? "").Contains(s))
This way you're checking that the element's value contains any of the strings in _filters. The use of the null coalescing operator ensures a NullReferenceException isn't thrown when the itemtype node doesn't exist since it is replaced with an empty string.
The other approach is to use let and filter out the nulls:
var q = from sr in SearchResults.Descendants("Result")
let itemtype = (string)sr.Element("itemtype")
where itemtype != null &&
_filters.Any(filter => itemtype.Contains(filter))
orderby (string)sr.Element("ipitemtype") ascending
select new SearchItem
{
//Create Object
ID = (string)sr.Element("blabla")
}
Note that String.Contains is case sensitive. So a check for "videos" won't match on "Videos" with a capital "V". To ignore case you can use String.IndexOf in this manner:
_filters.Any(filter => itemtype.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) >= 0)
You are checking if the array _filters has an element with the value "videos mission critial document advertising" (which is not the case), rather than if "videos mission critial document advertising" contains any of the elements in _filters.
Try this:
where _filters.Any(filter => ((string)sr.Element("itemtype")).Contains(filter))
The problem is that you are making false assumptions about the way the Contains method works. (Also see the String.Contains() documentation The contains method returns true if "a sequence contains a specific element". In the example you described, both the _filters array and the itemtype text contains the string videos, but neither contain each other. A more appropriate test would be to use the following extension method:
public static class ContainsAnyExtension
{
public static bool ContainsAny<T>(this IEnumerable<T> enumerable, params T[] elements)
{
if(enumerable == null) throw new ArgumentNullException("enumerable");
if(!enumerable.Any() || elements.Length == 0) return false;
foreach(var element in elements){
if(enumerable.Contains(element)){
return true;
}
}
return false;
}
}
So, your correct LINQ query would be:
var q = from sr in SearchResults.Descendants("Result")
where ((string)sr.Element("itemtype")).ContainsAny(_filters)
orderby ((string)sr.Element("ipitemtype")) ascending
select new SearchItem
{
ID = sr.Element("blabla").Value
};
It may also be helpful to review this post: How do I use LINQ Contains(string[]) instead of Contains(string), which is similar but specifically targets the String.Contains method instead of the Enumerable.Contains extension.

Why does this linq extension method hit the database twice?

I have an extension method called ToListIfNotNullOrEmpty(), which is hitting the DB twice, instead of once. The first time it returns one result, the second time it returns all the correct results.
I'm pretty sure the first time it hits the database, is when the .Any() method is getting called.
here's the code.
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value.IsNullOrEmpty())
{
return null;
}
if (value is IList<T>)
{
return (value as IList<T>);
}
return new List<T>(value);
}
public static bool IsNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value != null)
{
return !value.Any();
}
return true;
}
I'm hoping to refactor it so that, before the .Any() method is called, it actually enumerates through the entire list.
If i do the following, only one DB call is made, because the list is already enumerated.
var pewPew = (from x in whatever
select x)
.ToList() // This enumerates.
.ToListIsNotNullOrEmpty(); // This checks the enumerated result.
I sorta don't really want to call ToList() then my extension method.
Any ideas, folks?
I confess that I see little point in this method. Surely if you simply do a ToList(), a check to see if the list is empty suffices as well. It's arguably harder to handle the null result when you expect a list because then you always have to check for null before you iterate over it.
I think that:
var query = (from ...).ToList();
if (query.Count == 0) {
...
}
works as well and is less burdensome than
var query = (from ...).ToListIfNotNullOrEmpty();
if (query == null) {
...
}
and you don't have to implement (and maintain) any code.
How about something like this?
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value == null)
return null;
var list = value.ToList();
return (list.Count > 0) ? list : null;
}
To actually answer your question:
This method hits the database twice because the extension methods provided by the System.Linq.Enumerable class exhibit what is called deferred execution. Essentially, this is to eliminate the need for constructing a string of temporarily cached collections for every part of a query. To understand this, consider the following example:
var firstMaleTom = people
.Where(p => p.Gender = Gender.Male)
.Where(p => p.FirstName == "Tom")
.FirstOrDefault();
Without deferred execution, the above code might require that the entire collection people be enumerated over, populating a temporary buffer array with all the individuals whose Gender is Male. Then it would need to be enumerated over again, populating another buffer array with all of the individuals from the first buffer whose first name is Tom. After all that work, the last part would return the first item from the resulting array.
That's a lot of pointless work. The idea with deferred execution is that the above code really just sets up the firstMaleTom variable with the information it needs to return what's being requested with the minimal amount of work.
Now, there's a flip side to this: in the case of querying a database, deferred execution means that the database gets queried when the return value is evaluated. So, in your IsNullOrEmpty method, when you call Any, the value parameter is actually being evaluated right then and there -- hence a database query. After this, in your ToListIfNotNullOrEmpty method, the line return new List<T>(value) also evaluates the value parameter -- because it's enumerating over the values and adding them to the newly created List<T>.
You could stick the .ToList() call inside the extension, the effect is slightly different, but does this still work in the cases you have?
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if(value == null)
{
return null;
}
var result = value.ToList();
return result.IsNullOrEmpty() ? null : result;
}

Categories