I'm trying to write a lambda expression that looks at an array of objects ('fields') that may or may not have specific items in it. I would like to retrieve values if they exist; if no field in the array has a code of 'SomeCode', then there is no value to retrieve. I'm curious if there is a cleaner way to write the below, since I'll need to do this for a lot of fields and done want to run through the 'FirstOrDefault' call twice.
if (fields.FirstOrDefault(x => x.Code == "SomeCode") != null)
{
obj.CodeValue = fields.FirstOrDefault(x => x.Code == "SomeCode").Value;
}
EDIT: Thanks for any insight on doing this in a 'Lambda only' fashion; I'd like to improve my skills in this area and figured that there would be something cleaner than either calling it twice or simply assigning it to an interim object.
Just cache the return value from FirstOrDefault and then compare it.
var item = fields.FirstOrDefault(x => x.Code == "SomeCode");
if (item != null)
{
obj.CodeValue = item.Value;
}
In your current code you are querying twice, once for checking null, later to access value.
Try this:
var field = fields.FirstOrDefault(x => x.Code == "SomeCode");
if (field != null)
obj.CodeValue = field.Value;
Related
I am stuck with null values in my Datatable "articles". Using LINQ to get a list of articles works for column ArticleId but with column "ArticleVariations" the null values are killing me.
var result = this.articles.AsEnumerable().Where(r =>r.Field<String>("ArticleId").Equals(artNo)); // works. no nulls there ;)
var result = this.articles.AsEnumerable().Where(r =>r.Field<String>("ArticleVariations").Equals(artNo)); // stuck with nulls here
If the column contains nulls, I get an NullReferenceException, Can I avoid this somehow and is it possible to merge both expressions?
You can use null-conditional and null-coalescing operators:
var result = this.articles.AsEnumerable()
.Where(r =>r.Field<String>("ArticleVariations")?.Equals(artNo) ?? false);
The problem obviously occurs because r.Field<String>("ArticleVariations") retuns null. Thus you have to check for null before calling Equals on it.
For that you can call multiple statements within a LINQ-expression:
var result = this.articles.AsEnumerable().Where(r => {
var res = r.Field<String>("ArticleVariations");
if (res != null) return res.Equals(artNo);
else return false;
});
If the field can be null then just reverse your test:
var result = this.articles.AsEnumerable().Where(r => artNo.Equals(r.Field<String>("ArticleVariations")));
Then all you need to do is check that artNo is not null before making the call:
List<Type> result;
if (string.IsNullOrWhiteSpace(artNo))
{
result = new List<Type>();
}
else
{
result = this.articles.... as before
}
Where just takes a function which returns a bool to determine if it should filter an item out of a collection. You can write it with a multi-statement body just like any other function to make nulls easier to deal with. Something like this should be a good starting point:
.Where(r => {
string articleVariations = r.Field<string>("ArticleVariations");
return articleVariations != null && articleVariations.Equals(artNo);
});
IF you want to combine those checks somehow to build a list where one or the other of the given fields matches your artNo, you can just add it to the function body.
If the column contains nulls, I get an NullReferenceException, Can I avoid this somehow
Avoid using instance Equals methods where possible. Use the respective operators or static Equals methods because they handle correctly nulls for you.
In your concrete case, the easiest way is to replace Equals with ==:
var result = this.articles.AsEnumerable()
.Where(r => r.Field<string>("ArticleId") == artNo);
var result = this.articles.AsEnumerable()
.Where(r => r.Field<string>("ArticleVariations") == artNo);
and is it possible to merge both expressions?
It depends on what do you mean by "merging" them. If you mean matching article or variation by the passed artNo, then you can use something like this
var result = this.articles.AsEnumerable()
.Where(r => r.Field<string>("ArticleId") == artNo
|| r => r.Field<string>("ArticleVariations") == artNo);
I have basically achieved my desired effect through a not so elegant foreach loop. I am posting here for two reasons. One is if someone can show me a "cool" kid way to do it and or comment on reality as sometimes a foreach over an array is faster then casting to a List then using Lambda expressions.
So I am working with ExtendedAttributes property on the Artifact class.
https://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.artifact(v=vs.110).aspx
//I get my array of artifacts just fine
LinkFilter linkFilter = new LinkFilter();
linkFilter.FilterType = FilterType.ToolType;
linkFilter.FilterValues = new String[1] { ToolNames.WorkItemTracking }; //only work itms
Artifact[] artifacts = linkingService.GetReferencingArtifacts(changesetArtifactUris.ToArray(), new LinkFilter[1] { linkFilter });
//now I want to keep work items that are resolved or closed
//so I cast put into a List<T> just to then use Lambda in a for each loop
//THIS SECTION PSEUDO CODE FOR BREVITY yes I know you can't modify object you are looping over
var lst_artifacts = new List<Artifact>(artifacts);
foreach (var item in lst_artifacts)
{
lst_artifacts.RemoveAt(item.ExtendedAttributes.ElementAt(y => y.Value != "Resolved" || y.Value != "Closed"));
}
Thoughts?
Disclaimer: Not a cool kid.
What about using .Where() with a invert of your existing predicate:
authorsList = authorsList.Where(x => x.ExtendedAttributes.ElementAt(y => y.Value == "Resolved" || y.Value == "Closed")).ToList();
EDIT: Added .ToList() as I always forget it, thanks to #KyleJV
It seems that you are struggling on which function to use. You should use Any() instead of ElementAt(), your lambda y => y.Value != "Resolved" || y.Value != "Closed" is not a number, you cannot pass it into ElementAt(). You can use ElementAt(1), ElementAt(2) to get the element at specific index, but not ElementAt(true).
lst_artifacts.RemoveAll(item => !item.ExtendedAttributes.Any(y => y.Value == "Resolved" || y.Value == "Closed"));
It will remove all the items which do not have any resolved or closed attributes.
Is there a way to do the following using Linq:
foreach (var c in collection)
{
if (c.Condition == condition)
{
c.PropertyToSet = value;
// I must also check I only set this value to one minimum and only one element.
}
else
{
c.PropertyToSet = otherValue;
}
}
To clarify, I want to iterate through each object in a collection and then update a property on each object except for one element of my collection that should updated to another value.
At this moment I use a counter to check I set my value to one and only one element of my collection. I removed it from this example to let people suggest other solutions.
The original question without exception in collection is here
EDIT
I ask this question because I'm not sure it's possible to do it with LinQ. so your answers comfort my opinion about LinQ. Thank you.
You can use .ForEach to make the change, and .Single to verify only one element matches the condition:
// make sure only one item matches the condition
var singleOne = collection.Single(c => c.Condition == condition);
singleOne.PropertyToSet = value;
// update the rest of the items
var theRest = collection.Where(c => c.Condition != condition);
theRest.ToList().ForEach(c => c.PropertyToSet = otherValue);
I don't suggest you to implement this with Linq. Why? Because Linq is for querying, not for modification. It can return you objects which match some condition, or objects which don't match. But for updating those objects you still need to use foreach or convert query results to list and use ForEach extension. Both will require enumerating sequence twice.
So, simple loop will do the job:
foreach (var c in collection)
{
c.PropertyToSet = (c.Condition == condition) ? value : otherValue;
}
collection.Where(x => <condition>).ToList().ForEach(x => <action>);
Hacky way to use LINQ if you persist to use:
var result = collection.Select(c =>
{
c.PropertyToSet = c.Condition == condition ? value : otherValue;
return c;
});
But my recommendation, don't do this, you code actually get the best approach, for more readability, you can change:
foreach (var c in collection)
c.PropertyToSet = c.Condition == condition ? value : otherValue;
You can use a ternary operator in conjunction with a linq statement:
collection.ToList().ForEach(c => c.PropertyToSet = c.Condition == condition ? value : otherValue);
However I woud just use a regular foreach here to avoid converting the collection to a list.
Well, you could do:
var itemToSetValue = collection.FirstOrDefault(c => c.Condition == condition);
if(itemToSetValue != null)
itemToSetValue.PropertyToSet = value;
// Depending on what you mean, this predicate
// might be c => c != itemToSetValue instead.
foreach (var otherItem in collection.Where(c => c.Condition != condition))
{
otherItem.PropertyToSet = otherValue;
}
Now of course that's not a pure LINQ solution, but pure LINQ solutions are not appropriate for modifying existing collections.
I'm writing a query that uses FirstOrDefault after an OrderBy query, which should check if it isn't null first then use some data in it. Is there a better way than writing it like this:
int count = db.Items.Count(i =>
i.Assignments.OrderByDescending(a =>
a.DateAssigned).FirstOrDefault() != null
&&
i.Assignments.OrderByDescending(a =>
a.DateAssigned).FirstOrDefault().DateReturned == null)
What this code does is there are items that has many assignments, I take the latest assignment by date, then check if it exist, then run a condition on a property (DateReturned). As you see, this query is long, and most of my queries seem to look like this where I check for null first then run a second query on it using their properties. Is there a better way of doing this?
Just call .Any(a => a.DateReturned == null) to check whether there are any items that meet the condition.
If you only want to check the latest assignment, add .Take(1) before the .Any().
My take:
int count =
itemsQuery.Select(i => i.Assignments.OrderByDescending(a => a.DateAssigned))
.Count(i => i.FirstOrDefault() != null &&
i.First().DateReturned == null);
You can put the result in a variable to avoid doing the same thing twice:
int count = itemsQuery.Count(i => {
var f = i.Assignments.OrderByDescending(a => a.DateAssigned).FirstOrDefault();
return f != null && f.DateReturned == null;
});
Is this code good enough:
if (MyCollection.Where(p => p.SomeID == idstring).Any())
{
selectedval = MyCollection.Where(p => p.SomeID == idstring).FirstOrDefault().MyField;
}
My doubt is about the fact that I make the same query twice: first for null check and then to actually get data.
Maybe there is a better way to do this type of things?
Yes.
var item = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if (item != null)
selectval = item.MyField;
This avoids double querying the collection, which will certainly make a difference in big collections or if your collection performs a DB query.
There is. You can use the FirstOrDefault method which takes a predicate and returns null if the item is not found.
var result = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if( result != null )
{
// item was found
}