Looping though collection inside other collection and LINQ lambda expression - c#

Here I'm selecting descriptions from all FactoryOption with an Header of "TRANSMISSION"
tOptions = _vDetails.fOptions
.Where(x => (x.header != null && x.header.Value.ToUpper() == "TRANSMISSION"))
.Select(x => x.description)
.SelectMany(x => x);
If the header is null I would like to search for the header in ambiguous options which matches to "TRANSMISSION"
Something like the following :
foreach (var fOptions in _vDetails.fOptions)
{
if (fOptions.header != null && fOptions.header.Value.ToUpper() == "TRANSMISSION")
{
tOptions = fOptions.description;
}
else if (fOptions.ambiguousOption != null)
{
foreach (var ambiguousOption in fOptions.ambiguousOption)
{
if (ambiguousOption.header != null && ambiguousOption.header.Value.ToUpper() == "TRANSMISSION")
{
newseq = tOptions.Concat(ambiguousOption.description);
}
}
}
}
I am trying to change existing LINQ lambda expression for iterating through fOptions.ambiguousOption could someone please suggest.

If I'm understanding correctly I think you just want to do something like this:
var result = options.SelectMany(o => IsTransmissionHeader(o.header) ? o.description :
o.ambigousOptions == null || !o.ambigousOptions.Any(x => IsTransmissionHeader(x.header)) ? new string[] { } :
o.ambigousOptions.First(x => IsTransmissionHeader(x.header)).description)
.Where(d => d.Any());
I added a static method to check the header:
public static bool IsTransmissionHeader(Header header)
{
return header != null && header.Value != null && header.Value.ToUpper() == "TRANSMISSION"
}
This will return IEnumerable<string>. If you want a IEnumerable<IEnumerable<string>> change the SelectMany to Select.
EDIT:
To get all Transmission description values from ambigousOptions you need to change the last line so it looks like this:
var result = options.SelectMany(o => IsTransmissionHeader(o.header) ? o.description :
o.ambigousOptions == null || !o.ambigousOptions.Any(x => IsTransmissionHeader(x.header)) ? new string[] { } :
o.ambigousOptions.Where(x => IsTransmissionHeader(x.header)).SelectMany(x => x.description));

This should do what you want. Here I concatenate the descriptions from FactoryOption that have a matching header with the descriptions from AmbiguousOption that have a matching header.
var descriptions = details.SelectMany(d => d.FactoryOptions.Where(f => f.Header == "TRANSMISSION").Select(f => f.Description)
.Concat(d.FactoryOptions.SelectMany(f => f.AmbiguousOptions.Where(a => a.Header == "TRANSMISSION").Select(a => a.Description))));
Update
Answer above is for when you start with a collection of Detail. If you start with a single Detail then you can do:
_vehicleDetails.FactoryOptions.Where(f => f.Header == "TRANSMISSION").Select(f => f.Description)
.Concat(_vehicleDetails.FactoryOptions.SelectMany(f => f.AmbiguousOptions.Where(a => a.Header == "TRANSMISSION").Select(a => a.Description)));

Related

LINQ Query with method syntax

My requirement is to make boolean value (IsPC=true) only if I found any value with IsCurrent = true from the list and second condition is to filter the list with G or W codes and third condition is to check the PCBNumber length ==15 with only one from the list.
How short can i able to reduce the below query using LINQ method syntax
below is my query
var CurrentQ= p.List.Where(x => x.IsConCurrent== true);
if (CurrentQ.Count() > 0)
{
var NCurrentQwithWorQ = p.List.Where(x => x.Codes == Codes.W|| x.Codes== Codes.Q).Count();
if (NCurrentQwithWorQ != null)
{
var PCBNumber = p.List.Where(x => x.PCBNumber .Length == 15).Count();
if (PCBNumber == 1)
{
isPC = true;
}
}
}
You can use all conditions in same query like below,
var PCBNumber= p.List.Where(x => x.IsConCurrent== true && (x.Codes == Codes.W|| x.Codes== Codes.Q) && x.PCBNumber.Length == 15);
if (PCBNumber !=null && PCBNumber.Count() == 1)
{
isPC = true;
}
I'm not trying to debug what you wrote, but isn't this really what you're looking for--that is, daisy-chaining your Where conditions?
var isPC = p.List.Where(x => x.IsConCurrent == true).Where(x => x.Codes == Codes.W || x.Codes == Codes.Q).Where(x => x.PCBNumber.Length == 15).Count() == 1;
Both solutions suggested above are correct.
p.List.Where(x => x.IsConCurrent== true && (x.Codes == Codes.W|| x.Codes== Codes.Q) && x.PCBNumber.Length == 15);
p.List.Where(x => x.IsConCurrent == true).Where(x => x.Codes == Codes.W || x.Codes == Codes.Q).Where(x => x.PCBNumber.Length == 15).Count()
Actually they are performed in the same way. The Where function does not force immediate iteration through the data source. Only when you execute the Count function, LINQ will process row by row and execute criterion by criterion to find out which values should be calculated.
I can only suggest you add the Take(2) operator after the where clause. In this case LINQ will stop after finding the first two rows that matches provided criterion and other rows will not be processed.
p.List.Where(x => x.IsConCurrent == true)
.Where(x => x.Codes == Codes.W || x.Codes == Codes.Q)
.Where(x => x.PCBNumber.Length == 15)
.Take(2).Count()

Use same, looked-up value across multiple linq Where clauses without looking up more than once

I have a LINQ query which has three Where clauses. In each Where clause I am looking up the same set of items in order to compare values:
var items = _umbracoHelper.GetPage(ItemsPage.ModelTypeAlias).Children
.Where(x => level1Category == 0 || x
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id).Contains(level1Category))
.Where(x => !level2Categories.Any() || x
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id).Intersect(level2Categories.AsEnumerable()).Any())
.Where(x => !level3Categories.Any() || x
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id).Intersect(level3Categories.AsEnumerable()).Any());
Is there a way I can get the value of UmbracoAlias.Items.Categories once and store that value to be used in the other where clauses without causing the GetPropertyValue method to execute more than once?
You can pair up each item with category IDs, like this:
var items = _umbracoHelper.GetPage(ItemsPage.ModelTypeAlias).Children
.Select(c => new {
Child = c
, CategoryIds = c
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id)
.ToList()
})
.Where(x => level1Category == 0 || x.CategoryIds.Contains(level1Category))
.Where(x => !level2Categories.Any() || x.CategoryIds.Intersect(level2Categories.AsEnumerable()).Any())
.Where(x => !level3Categories.Any() || x.CategoryIds.Intersect(level3Categories.AsEnumerable()).Any())
.Select(x => x.Child);
This does the filtering on children paired up with their category IDs, and then keeps only the Child object in the final projection.
You could further simplify this by combining all three Where clauses:
var items = _umbracoHelper.GetPage(ItemsPage.ModelTypeAlias).Children
.Where(c => {
var categoryIds = c
.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)
.Select(y => y.Id)
.ToList();
if (level1Category != 0 && !categoryIds.Contains(level1Category)) {
return false;
}
if (level2Categories.Any() && !categoryIds.Intersect(level2Categories.AsEnumerable()).Any()) {
return false;
}
if (level3Categories.Any() && !categoryIds.Intersect(level3Categories.AsEnumerable()).Any()) {
return false;
}
return true;
});

C# Not all code paths return a valid in lambda expression of type func

First I did try this, my code:
timeReports = timeReports.OrderByDescending(x => x.Date)
.ThenByDescending(x => x.TimeReportDatas.First().StartHour)
.ToList();
But in my project all my projects won't have TimeReportDatas so it did crash due it containing no element.
So I changed the code to this:
timeReports = timeReports.OrderByDescending(x => x.Date).ThenByDescending(x =>
{
TimeReportData firstOrDefault = x.TimeReportDatas.FirstOrDefault();
if (firstOrDefault != null)
return firstOrDefault.StartHour;
}).ToList();
But than I'm left with the following error:
Not all code paths return a value in lambda expression of type
'Func'
Here you need to return something from all your path.
timeReports = timeReports.OrderByDescending(x => x.Date).ThenByDescending(x =>
{
var firstOrDefault = x.TimeReportDatas.FirstOrDefault();
if (firstOrDefault != null)
{
return firstOrDefault.StartHour;
}
else
{
return 0;
}
}).ToList();
Or more simpler way:
timeReports = timeReports.OrderByDescending(x => x.Date)
.ThenByDescending(x => (x.TimeReportDatas.FirstOrDefault() != null) ? x.TimeReportDatas.First().StartHour : 0)
.ToList();
timeReports = timeReports.OrderByDescending(x => x.Date).ThenByDescending(x =>
{
TimeReportData firstOrDefault = x.TimeReportDatas.FirstOrDefault();
if (firstOrDefault != null)
return firstOrDefault.StartHour;
return 0;
}).ToList();
If firstOrDefault is null then return "0" or new element with standart data. Or use Where(...) for lambda and block "If" - not needed.

MVC4 Linq Query Optimisation

I have the below code which works but I do not feel this is the best way to achieve the result. I am looking at optimising my code. Any suggestions of a better option will be appreciated. sub is a subcategory which is nullable.
[AllowAnonymous]
public ActionResult _relatedgrps(string cat, string sub)
{
if (!string.IsNullOrWhiteSpace(sub)){
var pgs = db.Pages
.Where(u=>u.MetaNoSearch==false)
.Where(u => u.PaOk == true && u.Category.Name == cat && u.SubCategory.CatName == sub)
.OrderByDescending(u => u.PaCreatedOn);
return PartialView(pgs.ToList());
}else{
var pgs = db.Pages
.Where(u=>u.MetaNoSearch==false)
.Where(u => u.PaOk == true && u.Category.Name == cat )
.OrderByDescending(u => u.PaCreatedOn);
return PartialView(pgs.ToList());
}}
Linq IEnumerables can be additive and the query will only be executed when enumerated for the first time (like calling .ToList()). So you should be able to do something like this:
var pgs = db.Pages
.Where(u => u.MetaNoSearch == false &&
u.PaOk == true &&
u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
{
pgs = pgs.Where(u => u.SubCategory.CatName == sub);
}
return PartialView(pgs.OrderByDescending(u => u.PaCreatedOn).ToList());
Create an object to query it. To improve it, you also could remove it boolean comparations because they are conditions.
var query = db.Pages.Where(u => !u.MetaNoSearch && u.PaOk && u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
query = query.Where(u => u.SubCategory.CatName == sub);
query = query.OrderByDescending(u => u.PaCreatedOn);
return PartialView(query.ToList());
#user3021830 - be careful with String.IsNullOrWhitespace, you cannot use that in a database query. You could do String.IsNullOrWhitespace(sub), but not String.IsNullOrWhitespace(u.*).
I'd avoid any conditionals in the query because that will likely result in a case statement in the SQL.
To produce the best SQL I'd do something like this:
var pgs = db.Pages.Where(u => u.MetaNoSearch == false)
.Where(u => u.PaOk == true)
.Where(u => u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
{
pgs = pgs.Where(u => u.SubCategory.CatName == sub);
}
var result = pgs.OrderByDescending(u => u.PaCreatedOn).ToList();

How to attach a set of 'And' filters to a QueryOver query?

My method has a parameter which will determine which column to perform a where filter on, so to make things generic I need to be able to perform some logic on which column I want to perform the where on.
Is it possible to attach a .And clause to a given QueryOver<> query?
public List<..> GetABC(SomeType type)
{
NHibernateHelper.Session.QueryOver<Blah>()
.Where(x => x.name = "")
.And(x => x.a) // if type == SomeType.A then x.a, otherwise x.b (SomeType.B)
}
How could I do this?
I know when doing a criteria query I could create a criteria and then attach it to the query.
Sure.
var query = NHibernateHelper.Session.QueryOver<Blah>()
.Where(x => x.name = "");
if(type == SomeType.A)
{
query = query.And(x => x.a == ...);
}
else
{
query = query.And(x => x.b == ... );
}
The query will only be executed after the ".List()"
Update: I don't know if you're refering to something like this (in your comment)
var query = NHibernateHelper.Session.QueryOver<Blah>()
.Where(x => x.name = "");
Expression<Func<Blah, bool>> typePredicate = null;
if(type == SomeType.A)
{
typePredicate = x => x.a == ...;
}
else
{
typePredicate = x => x.b == ...;
}
query = query.Where(typePredicate);
Or probably you're more interested in something in the likes of Detached queries?
Not sure I completely understand the question ... but something like this?
public List<..> GetABC(SomeType type)
{
NHibernateHelper.Session.QueryOver<Blah>()
.Where(x => x.name = "")
.And(x => typeof(type) == SomeType.A ? x.a : x.b)
}

Categories

Resources