Turn 2 linq expressions into - linq

Is there anyway to change the following 2 linq expressions into 1?
var criticalCategories =
_commonDao.GetAllByExpression<CategoryItem>(
x => x.Category.Uid == gridAnswer.ActivityCategory.Uid && x.Critical);
if(criticalCategories.Any())
{
criticalWeight = criticalCategories.Min(x => x.Weight);
}

You can use Enumerable.DefaultIfEmpty to make sure that Min will produce a specific value if your source sequence contains no elements.
You could then write:
var criticalCategories = _commonDao.GetAllByExpression<CategoryItem>(...);
criticalWeight = criticalCategories
.Select(x => x.Weight)
.DefaultIfEmpty(42)
.Min();
The above is trivially chainable, but I did not actually chain it here because I 'm not quite sure how criticalCategories is supposed to be used later on (if at all). Could you please clarify?

Related

Using Linq, How can I properly append multiple Where clauses so that they appear in the appropriate order?

The order I would like the end result to appear in is Exact Matches first given a input string, followed by other matches that are Contains for a given field. I tried to approach this in a very rudimentary way as shown here in this example:
var raw = Model.SearchResults.Where(m => m.EffectiveDateTime != null).OrderBy(m => m.EffectiveDateTime).ToList();
var exact = raw.Where(m => m.IssueNumber.ToLower() == Model.SearchText.ToLower());
var contains = raw.Where(m => m.IssueNumber.ToLower().Contains(Model.SearchText.ToLower()));
var list = exact.Union(contains);
This approach seems like it'd be a really bad way to do this. In fact, the Union portion seems to effectively crash my application. Is there an opposite to Intersection which would give me the remaining results outside the Exact matches that I could then append to a final list so that the order would be Exact Matches followed by StartsWith matches followed finally by Contains matches in that descending order?
To answer your original question, you can use a temporary expression to classify the match types, then order by the match type and other criteria, and it will translate to SQL as well:
var st = Model.SearchText.ToLower();
var list = Model.SearchResults.Where(m => m.EffectiveDateTime != null)
.Select(m => new {
m,
im = m.IssueNumber.ToLower()
})
.Select(mim => new {
mim.m,
Rank = mim.im == st ? 1 : mim.im.StartsWith(st) ? 2 : mim.im.Contains(st) ? 3 : 4
})
.Where(mr => mr.Rank < 4)
.OrderBy(mr => mr.Rank)
.ThenBy(mr => mr.m.EffectiveDateTime)
.Select(mr => mr.m)
.ToList();
I did the double Select to emulate let from fluent syntax, which I think is a bit clearer than lambda syntax in this case:
var lisx = (from m in Model.SearchResults
where m.EffectiveDateTime != null
let im = m.IssueNumber.ToLower()
let Rank = im == st ? 1 : im.StartsWith(st) ? 2 : im.Contains(st) ? 3 : 4
where Rank < 4
orderby Rank, m.EffectiveDateTime
select m)
.ToList();
Also, if you do the whole query in the database, the ToLower is likely unnecessary, as the default for SQL is probably to be case-insensitive anyway.
Actually, I went back to the drawing board and figured it out. This is a little bit better for me and returns the results I needed.
var list = Model.SearchResults
.Where(e => e.A.ToLower().Contains(Model.SearchText.ToLower()))
.GroupBy(d => new { d.A, d.B, d.C})
.OrderBy(x => x.Key.A)
.ThenBy(x => x.Key.B)
.ThenBy(x => x.Key.C)
.Select(x => new
{
A= x.Key.A,
B= x.Key.B,
C= x.Key.C
})
.ToList();

Using StringComparison.OrdinalIgnoreCase to ignore Case

This code is case sensitive, how to make it case insensitive?
return HeaderNames.Length == fileLine.Count &&
HeaderNames
.Select(headItem => fileLine[Array.IndexOf(HeaderNames, headItem)] == headItem)
.All(i => i);
Thanks for your answers/
How about using FindIndex instead of IndexOf.
I assume you have array of strings
Array.FindIndex(HeaderNames, t => t.IndexOf(headItem, StringComparison.InvariantCultureIgnoreCase) >=0);
Example:
var a = new string[]{"green","red"};
var idx = Array.FindIndex(a, t => t.IndexOf("Red", StringComparison.InvariantCultureIgnoreCase) >=0);
Console.WriteLine(idx);
Output
1
Note: You could also use string.Equals instead of t.IndexOf in above suggestion
t=>string.Equals(t, headItem, StringComparison.InvariantCultureIgnoreCase)
It will be a long nested LINQ though.
Edit
If you want to find exact string i.e. dont want to get index of «ed» from «Red» as item existing in array. Use string.Equals.
There is no reason to index back into the array to get the position, there is a version of Select for that. You can use String.Equals and pass the StringComparison option:
return HeaderNames.Length == fileLine.Count &&
HeaderNames.Select((headItem, i) => fileLine[i].Equals(headItem, StringComparison.OrdinalIgnoreCase)).All(i => i);

Setting visiblilty of control using LINQ

I'm working on refactoring some code to practice LINQ. For some reason I can't get this code to cooperate.
//ActionControls is a ControlCollection
var actionControls = flowLayoutPanel1.FilterControls(c => c is Button);
//TODO: Optimize
foreach(var control in actionControls)
{
control.Visible = workingItemDataTable.AsEnumerable().Any(row => "btn" + row.Field<string>("Name") == control.Name);
}
What I am trying to do now.
flowLayoutPanel1.FilterControls(c => c is Button && c.Name == "btnTaskInfo"//btnTaskInfo is always visible
|| workingItemDataTable.AsEnumerable().Any(row => "btn" + row.Field<string>("Name") == c.Name)).Cast<Button>()
But after casting this as a button, I can not figure out how to set visible = false. Any Advice?
You'd still need to iterate the controls, but you can probably do this, assuming FilterControls isn't much more than an alias for Where:
var actionControls = flowLayoutPanel1.OfType<Button>();
There are some "tricks" and shortcuts for iterating and doing sets within a lambda expression, but it makes the code just look messy and they are mostly just hacks. You can create your own extension ForEach (if there isn't one available to you if you really want to do it anyway).
flowLayoutPanel1.OfType<Button>().ForEach(btn=>{btn.Visible= ... });

Can this code be converted into a single linq statement?

I'm trying to filter a list within a list from an entity framework entity.
I've managed to get the code working however, i'm not convinced it's the cleanest way of achieving the goal.
Here's the code I have so far:
foreach (var n1 in tier.MatchNodes)
{
n1.LenderMatchNodes = n1.LenderMatchNodes.Where(x => x.Commission == 0).ToList();
}
Effectively MatchNodes contains a collection of LenderMatchNodes, however I want to return only the nodes where the commission == 0.
Thanks in advance.
Try
tier.MatchNodes.ToList().ForEach(n1=>n1.LenderMatchNodes = n1.LenderMatchNodes.Where(x => x.Commission == 0).ToList());
Try using SelectMany():
var result = dataContext.Table<Tier>()
.Where(some condition to get you the tier)
.SelectMany(tier => tier.MatchNodes)
.SelectMany(node => node.LenderMatchNodes)
.Where(x => x.Commission == 0)
.ToList();
This has the additional benefit of being able to execute it a single SQL query.
If you're goal is to actually update the node list in the database, you can still minimize the number of queries using Include() (assuming you're using EF):
var nodes = dataContext.Table<Tier>()
.Where(some condition to get you the tier)
.SelectMany(tier => tier.MatchNodes)
.Include(node => node.LenderMatchNodes) // loads this eagerly
.ToList();
nodes.ForEach(n => n.LenderMatchNodes = n.LenderMatchNodes.Where(condition));

minimum value in dictionary using linq

I have a dictionary of type
Dictionary<DateTime,double> dictionary
How can I retrive a minimum value and key coresponding to this value from this dictionary using linq ?
var min = dictionary.OrderBy(kvp => kvp.Value).First();
var minKey = min.Key;
var minValue = min.Value;
This is not very efficient though; you might want to consider MoreLinq's MinBy extension method.
If you are performing this query very often, you might want to consider a different data-structure.
Aggregate
var minPair = dictionary.Aggregate((p1, p2) => (p1.Value < p2.Value) ? p1 : p2);
Using the mighty Aggregate method.
I know that MinBy is cleaner in this case, but with Aggregate you have more power and its built-in. ;)
Dictionary<DateTime, double> dictionary;
//...
double min = dictionary.Min(x => x.Value);
var minMatchingKVPs = dictionary.Where(x => x.Value == min);
You could combine it of course if you really felt like doing it on one line, but I think the above is easier to read.
var minMatchingKVPs = dictionary.Where(x => x.Value == dictionary.Min(y => y.Value));
You can't easily do this efficiently in normal LINQ - you can get the minimal value easily, but finding the key requires another scan through. If you can afford that, use Jess's answer.
However, you might want to have a look at MinBy in MoreLINQ which would let you write:
var pair = dictionary.MinBy(x => x.Value);
You'd then have the pair with both the key and the value in, after just a single scan.
EDIT: As Nappy says, MinBy is also in System.Interactive in Reactive Extensions.

Resources