Get All instead of FirstOrDefault - c#

I have the following query:
PromotionList dataPromotion = authenticateCustomerResponseRootObject.Response
.AuthenticateCustomerResponse.EligiblePromotions.PromotionList
.Where(p => p.LineOfBusiness.ToUpper().Equals("DATA"))
.FirstOrDefault();
My PromotionList contains 3 objects, with LineOfBusiness Data,Video and third object contains Data too. Above query returns PromotionList of first object only whereas I want ALL whose LineOfBusiness is equal to Data. Why is it happening?

You are using FirstOrDefault so you are returning only the first.
PromotionList dataPromotion = authenticateCustomerResponseRootObject.Response.AuthenticateCustomerResponse.EligiblePromotions.PromotionList.Where(p => p.LineOfBusiness.ToUpper().Equals("Data")).FirstOrDefault();
If you want all of them just remove that call at the end and replace with a ToList, ToArray or similar that meets your needs:
var data = authenticateCustomerResponseRootObject.Response.AuthenticateCustomerResponse.EligiblePromotions.PromotionList.Where(p => p.LineOfBusiness.ToUpper().Equals("Data")).ToList();
Also as mentioned in the comments your Where call uses ToUpper then compares on a string containing lower case characters so will never return any results. You either need to remove the ToUpper, use a upper case string or even use ignore case:
Where(p => p.LineOfBusiness.Equals("Data", StringComparison.OrdinalIgnoreCase))

Related

Skip an empty sequence inside a LINQ .Where statement

I have an array of Tuple arrays like this:
(Type, Func<int, bool>)[][] Formats;
I also have a Type typ and a int i.
I then need an IEnumerable<(Type, Func<int,bool>[])> (aka return items) where a potential match is evaluated by Item1 (i.e. Type) matching typ in the format .IsAssignableFrom(typ). If we get a match, then we aren't done: we only include it if Item2.Invoke(i) evaluates to true.
var results = Formats.Where(f => f.Where(o => o.Item1.IsAssignableFrom(typ)).First().Item2.Invoke(i));
This is not valid because I think if a return item doesn't have an inner item to retrieve in the .First() call, it throws an InvalidOperationException "Sequence contains no data" because you can't call .First() on an empty sequence.
Can you help restate this Linq to ignore the sequence if empty?
This is a perfect opportunity to use the Enumerable.FirstOrDefault() method:
Returns the first element of a sequence, or a default value if no element is found.
In this case, the "default value" would be (null, null). So, use FirstOrDefault() instead of First(), and then ignore items appropriately. Something like this might work:
var results = Formats.Where(f => f.Where(o => o.Item1.IsAssignableFrom(typ)).FirstOrDefault().Item2?.Invoke(i) ?? false);

Linq query “where [column] in (list of values)” that return single value for each values?

Let say we have this LinQ code (in LinqPad):
List<string> list = new List<string>();
list.Add("1611080010");
list.Add("1611080011");
list.Add("WRONGID");
var result = Orders.AsQueryable().Where(y => list.Contains(y.Id));
// And yes my Ids are string for this sample
result.Dump(); // To display the result in LinqPad
result.Count(); // equal 2
Is it possible to improve this query to force the system to return one element for each of my list element or throw an exception? So if I have 3 values in my list I should have 3 values in my result?
On your question: Is it possible to improve this query to force the system to return one element for each of my list element or throw an exception:
var result = Orders.AsQueryable().SingleOrDefault(y => list.Contains(y.Id));
SingleOrDefault
Returns the only element of a sequence, or a default value if the
sequence is empty; this method throws an exception if there is more
than one element in the sequence.
You can use any one from below one
Whenever you use SingleOrDefault, you clearly state that the query should result in at most a single result. On the other hand, when FirstOrDefault is used, the query can return any amount of results but you state that you only want the first one.
I personally find the semantics very different and using the appropriate one, depending on the expected results, improves readability.
var result = Orders.AsQueryable().SingleOrDefault(y => list.Contains(y.Id));
var result = Orders.AsQueryable()..FirstOrDefault(y => list.Contains(y.Id));
If I understand your question correctly you want to return one object from Orders where the Id of the object equals an Id in your list and if an Id in the list is not present in the Orders object an exception should be thrown.
To achieve that you could do:
var result = list.Select(id => Orders.AsQueryable().First(y => y.Id == id)).ToList();
This will throw an exception if an Id from the list has no match in the Orders object. If all Ids are found then your result will contain the same number of elements as there are Ids in the list.
Another option would be to use:
var result = list.Select(id => Orders.AsQueryable().FirstOrDefault(y => y.Id == id)).ToList();
This would not throw an error, but it would always return the same number of elements as there are Ids in the list. The Ids that would not be found would have a null entry though.

returning default value if a property is null

As you know there's an extension method called DefaultIfEmpty, based on the documentation:
DefaultIfEmpty:
Returns the elements of the specified sequence or the type parameter's
default value in a singleton collection if the sequence is empty.
So in this case if the sequence is empty, it returns a default value, for example, take a look at this answer:
opencall.Priority = averages.Where(x => x.ProblemCode == opencall.ProblemCode)
.Select(x => x.Priority)
.DefaultIfEmpty("")
.Single();
in this example if the averages is empty, it returns an empty string, But I want to know is there something like an extention method in linq so that if the preperty (x.Priority) is null, returns a default value?
PS: I know we can check that using a if statement:
opencall.Priority = averages.Where(x => x.ProblemCode == opencall.ProblemCode)
.Select(x => x.Priority)
.DefaultIfEmpty("")
.Single();
if (!string.IsNullOrWhiteSpace(opencall.Priority))
opencall.Priority = "Default value";
...
I'm just curious to know that, Is there any kind of extension method for doing that?
You can use the same DefaultOrEmpty method overload to provide the default value. In the above query since we are trying to fetch the Priority property which is of String type. You can provide a default value of String in the method overload:-
opencall.Priority = averages.Where(x => x.ProblemCode == opencall.ProblemCode)
.Select(x => x.Priority)
.DefaultIfEmpty("High")
.Single();
This will result in High for non matching rows.

How to Get a Object from IEnumerable collection using LINQ Lambda?

storageColl is having a IStorage with property "Id" as "Test".
What I am doing-
string id="Test";
IEnumerable<IStorageObject> storageColl = getStorageCollection();
IStorageObject storageObject = storageColl.ToList().Where(m => m.Properties["Id"] == id)
.ToList()
.Cast<IStorageObject>().ToArray()[0];
Is there a better way to do this. As this may throw array out of bound exception if the storageColl will not have that "Test".
You can use FirstOrDefault on the IEnumerable.
var storageObject = storageCol1.Where(m => m.Properties["Id"] == id).FirstOrDefault();
Or as David Hedlund pointed out, use the predicate overload on FirstOrDefault and remove the Where.
var storageObject = storageCol1.FirstOrDefault(m => m.Properties["Id"] == id);
Your storageColl is a sequence of objects that implement IStorageObject. The use of the Where only limits the elements you get when you enumerate over the sequence, it does not change them.
It is a waste of processing power to convert the sequence to a list when you only need the first element of the sequence or the a subset of it.
Familiarize yourself with the following Ling functions:
Any() returns true if the sequence contains at least one element
Any( item => ....) return true if any of the elements in the sequence meets the requirement
First() returns the first element of the sequence. Exception if not Any()
FirstOrDefault returns the first element of the sequence or the default (usually null) if not Any()
The nice thing about these functions is that they don't have to enumerate over all elements in the sequence, but can stop as soon as they found something.
If you use ToList() the code enumerates over all elements, throws most of them away and uses only the first element. FirstOrDefault() would have stopped after the first enumeration.
since the collection is implement IStorageObject you don't neet to cast them and for get item by index you can use any class that utilizes Array or IList
since LINQ operates on IEnumerable (Array itself is enumerable->to iterate) you don't need to cast them to array.you can utilize ElementAt method or Use IList classes (as List)
IStorageObject storageObject = storageColl.Where(m => m.Properties["Id"] == id).First();
You can simply achieve this by
var result = storageColl.Where(m => m.Properties["Id"] == id);
You should check firstOrDefault because can return null.
var Object = storageCol.Where(p => p.Properties["Id"] == id).FirstOrDefault();
if(Object != null)
{
// Do some Work
}

Logic behind updating object List using linq

I have a list of POCO objects, why is it that the following code:
elements.Where(x => x.Param1 == "M").Select(x => x.Param2= "").ToList();
(TL;DR; sets param2 = "" on every element which param1 equals M)
updates the enumerable while this one:
elements.Where(x => x.Param1 == "M").Select(x => x.Param2= "");
does not update it?
Notice that i'm doing neither elements = elements.Where... nor var results = elements.Where...
Your second code snippet without ToList is just a query. You need to iterate to actually execute it. Calling ToList executes the original query and since in your Select you are modifying a property of an object, you see the effect (or a side effect) in your original list. It is related to parameter passing in C#. Since your lambda expression in Select is an anonymous method which is receiving a parameter object of the list. Later when you modify one of it's property you see the effect.
Similarly if you try to set the object to null you will not see the side effect.
.Select(x => x = null).ToList();

Categories