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.
Related
I have a list List<OfferComparison> Comparison. I want to
check if all the items have Value == null in an if condition.
How can I do it with linq?
public class OfferComparison : BaseModel
{
public string Name { get; set; }
public string Value { get; set; }
public bool Valid { get; set; }
}
Updated (post C# 7) Answer
If using C# 7 or 8 then one could use the is keyword together with Linq.All:
var result = Comparison.All(item => item.Value is null)
If using C# 9 then one could use the is not null together with Linq.Any:
var result = Comparison.Any(item => item.Value is not null)
If using C# 9 then one could also use the is object or is {} together with Linq.Any:
var result = Comparison.Any(item => item.Value is object)
All these options are somewhat equivalent. At least in terms of time complexity they are all O(n). I guess the "preferred" option simply depends on personal opinion.
Original (pre C# 7) Answer
Using linq method of All:
var result = Comparison.All(item => item.Value == null)
Basically what it does is to iterate all items of a collection and check a predicate for each of them. If one does not match - result is false
You can check by this linq statement
var isNull = Comparison.All(item => item.Value == null);
I'm not totally sure about the internal differences of All and Exists, but it might be a good idea to just check whether one of the entries is not null and then negate the result:
var result = !Comparison.Exists(o => o.Value != null);
I would expect this query to quit after the first non-null value was found and therefore to be a little more efficient.
Update: From the Enumerable.All documentation:
The enumeration of source is stopped as soon as the result can be determined.
Therefore, using All will probably not result in the entire list getting processed after a non-null value has been found.
So the aforementioned possible performance gain is not likely to occur and both solutions probably do not differ.
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.
}
Hi my code is as below:
List<COSIS_DAL.PaymentsHeader> pheadr = c.GetPaymentHeaderByPayDate(PayDate);
public List<PaymentsHeader> GetPaymentHeaderByPayDate(DateTime payDate)
{
CosisEntities db = new CosisEntities();
IQueryable<PaymentsHeader> query = (from p in db.PaymentsHeaders
where p.PaymentDate.Value.Day == payDate.Day
&& p.PaymentDate.Value.Month == payDate.Month
&& p.PaymentDate.Value.Year == payDate.Year
select p);
return query.ToList();
}
so I want to save the data from pheadr to a string like this:
string keyword = Convert.ToString(pheadr.Select(m => m.VoucherNumber));
but I am not getting the value inside the list. Instead of the value I am getting this:
System.Linq.Enumerable+WhereSelectListIterator`2[COSIS_DAL.PaymentsHeader,System.String]
Please help me on this. I am really in trouble for this. Thanks in advance.
The problem is that pheadr.Select(m => m.VoucherNumber) isn't a single value... It's a collection of values... You could try:
string keyword = string.Join(", ", pheadr.Select(m => Convert.ToString(m.VoucherNumber)));
if you really want multiple voucher numbers separated by a ,.
or you could put it in a separate collection:
List<string> vouchers = pheadr.Select(m => Convert.ToString(m.VoucherNumber)).ToList();
Maybe something like this:
var keywords = pheadr.Select(x=>x.VoucherNumber.ToString()).ToList();
It will select all voucher numbers to list of strings. Is it what you wanted? keywords is List<string>
The string you are getting is because you are converting Linq expression to string, not inner field.
For only first record, use:
var keyword = Convert.ToString(pheadr.First().VoucherNumber);
If you are not sure that it will always return one value try:
var ph = pheadr.FirstOrDefault();
var keyword = ph!=null?Convert.ToString(ph.VoucherNumber):"";
EDIT:
as #xanatos suggested I used Convert instead ToString
Since pheadr is a collection of PaymentsHeader objects, the line
pheadr.Select(m => m.VoucherNumber)
will select each PaymentsHeader VoucherNumber property, which I'll assume is a string. So, the thing that will be returned will be a collection of strings. You can loop through it, you can cast it to a list or an array, or you can stick it in a stew, but you can't just call .ToString on it. That invokes the default object.ToString behaviour that just dumps the name of the class, which is what you got.
If you want to extract, for example, a comma-separated list of those strings, you can use:
string.Join(",", pheadr.Select(m => m.VoucherNumber))
try this
string keyword ;
if(pheader != null)
keyword = pheader.FirstOrDefault().VoucherNumber;
else
keyword = "";
Is it possible to do something like the following in Linq?
List<Group> groupsCollection = new List<Group>();
groups.Select( g => {
String id = g.Attributes["id"].Value;
String title = g.Attributes["title"].Value;
groupsCollection.Add( new Group(id, title) );
} );
This is just an example. I know a Foreach loop would be sufficient but I was querying whether it would be possible or not.
I tried it, and it's saying:
Cannot convert lambda expression to type System.Collections.IEnumerable<CsQuery.IDomObject> because it's not a delegate type
Edit: Group is my custom class. Groups are a collection of CsQuery Dom Objects. You can think of them as a collection of html anchor elements. They are IEnumerable.
I think you're looking for this:
groupsCollection = groups.Select(g =>
new Group(g.Attributes["id"].Value,
g.Attributes["title"].Value)).ToList();
Explanation:
Select() projects each element of a sequence into a new form. (From MSDN)
It's basically a transformation function that takes one IEnumerable and transforms it, in whatever way you like, to another IEnumerable, which seems to be exactly what you want here.
Select() takes a lambda expression that returns a value; it shouldn't have any sife effects.
You want
var groupsCollection = groups.Select(g => {
return ...;
}).ToList();
You can use the constructor to initialize it with the query:
var query = groups.Select( g => {
String id = g.Attributes["id"].Value;
String title = g.Attributes["title"].Value;
return new Group(id, title);
});
List<Group> groupsCollection = new List<Group>(query);
Do not modify colections in a linq-query, instead select data that can be used to modify collections.
As you said, you know using ForEach would be relevant. However you are just curious if you can do it another way. As others pointed out, you can use Select but the code in the {...} should return some value. However, you can do it this way which is better if you want to stop or break the loop depending on some condition using TakeWhile:
groups.TakeWhile( g => {
String id = g.Attributes["id"].Value;
String title = g.Attributes["title"].Value;
groupsCollection.Add( new Group(id, title) );
return true;//If you want to break the loop, just return false accordingly.
});
I have this class:
class OriginalClass {
string FirstItem;
List<string> ListOfSecondItems;
}
I want to convert the list of one class into the list of another, or to "flatten" this class into new one:
class FlattenedClass {
string First;
string Second;
}
I'm using this LINQ expression to convert from one to another:
OriginalClass original;
var flattened = from Item in original
from secondItem in item.ListOfSecondItems
select new FlattenedClass(item.FirstItem, secondItem);
The problem is if list of second items is empty I lose the first item. I want to have some "(default)" value to be used if the list is null or empty. I guess DefaultIfEmpty is the key, but don't know how to incorporate it into existing query.
This call to DefaultIfEmpty says: "If that ListOfSecondItems is empty, use a single null instead."
var flattened =
from Item in original
from secondItem in item.ListOfSecondItems.DefaultIfEmpty()
select new FlattenedClass(item.FirstItem, secondItem);
Your question mentions the possibility that ListOfSecondItems might be null. This code handles that possibility. It also supplies a default string instead of null (using the other version of DefaultIfEmpty).
var flattened =
from Item in original
let subList = item.ListOfSecondItems ?? new List<string>()
from secondItem in subList.DefaultIfEmpty("(default)")
select new FlattenedClass(item.FirstItem, secondItem);