c# linq conditional union - c#

hi I have the following code and would like to only make each union if a condition is true.
I Know I could write a select of if else but wanted to know if there is a slicker Linq way!?
//join the list into one and sort by seqnumber
SegmentList = Alist.Cast<BaseSegment>()
.Union(BList.Cast<BaseSegment>()).Union(CList.Cast<BaseSegment>())
.OrderBy(item => item.SegSeqNumber).ToList();
So given the above if ATest =true how do I only iclude Alist like wise if BTest && CTest are true how do I include only BList and Clist
Thanks

To do it in a LINQ style way with your checkboxes, something like:
SegmentList = Alist.Where(i => checkbox1.IsChecked).Cast<BaseSegment>()
.Union(BList.Where(i => checkbox2.IsChecked).Cast<BaseSegment>())
.Union(CList.Where(i => checkbox3.IsChecked).Cast<BaseSegment>())
.OrderBy(item => item.SegSeqNumber).ToList();
would work. But I don't think it is either very understandable or efficient.

Something like this?
SegmentList = Alist.Cast<BaseSegment>()
.Union(includeB ? BList.Cast<BaseSegment>() : Enumerable.Empty<BaseSegment>())
.Union(includeC ? CList.Cast<BaseSegment>() : Enumerable.Empty<BaseSegment>())
.OrderBy(item => item.SegSeqNumber)
.ToList();
This is not identical to your original (it will remove duplicates from Alist no matter what) but should be what you want.
For any more than 2 conditional unions, you would probably want a different query, something like:
var listsByCb = new Dictionary<CheckBox, MyListType>
{{ aListBox, aList}, {bListBox, bList}, {cListBox, cList}};
var segmentList = listsByCb.Where(kvp => kvp.Key.Checked)
.SelectMany(kvp => kvp.Value.Cast<BaseSegment>())
.Distinct();
.OrderBy(item => item.SegSeqNumber)
.ToList();

try something like
SegmentList = Alist.Cast<BaseSegment>().Where(z=>ATest)
.Union(BList.Cast<BaseSegment>().Where(x=>Btest)).Union(CList.Cast<BaseSegment>().Where(c=>Ctest))
.OrderBy(item => item.SegSeqNumber).ToList();
where class in each case will return all elements if corresponding test is true and will return no element otherwise. its completely untested though

Use a Where() clause for that, like following:
//join the list into one and sort by seqnumber
SegmentList =
Alist.Cast<BaseSegment>().Where(a => ATest(a))
.Union(
BList.Cast<BaseSegment>(.Where(b => BTest(b))
.Union(CList.Cast<BaseSegment>().Where(c => CTest(c))
.OrderBy(item => item.SegSeqNumber).ToList();
By the way, do you really need the last ToList()?

Related

How to use LINQ to adjust result?

From this LINQ:
var inspectionItems = inspArchive.Select(x => x.InspectionItems);
I get this result in inspectionItems :
My question is how can I make using LINQ in elegant way to make from inspectionItems result above one array distincted by Id.
Like that:
You want the SelectMany() LINQ method:
var inspectionItems = inspArchive.SelectMany(x => x.InspectionItems);
If you only want distinct items by a particular property, then you can either implement an equality comparer to see if your inspection classes are equal, then call .Distinct(myInspectionEqualityComparer) at the end of your method chain, or you can do this:
var distinctInspectionItems = inspArchive.SelectMany(x => x.InspectionItems)
.GroupBy(i => i.Id)
.Select(group => group.First());
Without seeing more of your code, I think you want to use SelectMany and Distinct ?
var inspectionItems = inspArchive.SelectMany(x => x.InspectionItems).Distinct();
The question is unclear, but check if this works for you:
var inspectionItems = inspArchive.SelectMany(x => x.InspectionItems)
.GroupBy(x => x.ID)
.Select(g => g.First())
.ToArray();

How to use Linq to sort an array by Length and then value

Given the following values in order :
011124
01112
011123
1905
How could I use Linq to sort this:
List<string> values = new List<string>() { "011124", "01112", "011123", "1905" };
.. so it is effectively this:
List<string> values = new List<string>() { "011124", "011123", "01112", "1905" };
(updated: added '1905' as the previous demo would have sorted easily with an int sort -- sorry for the confusion)
Try this snippet:
var sortedValues = values
.OrderByDescending(x => x.Length)
.ThenByDescending(x => Convert.ToInt32(x));
If you really need use it as a List, then add ToList() at the end.
I'm going to assume you actually care about the value of each integer. If so:
var sortedValues = values.OrderByDescending(x => x.Length)
.ThenByDescending(x => int.Parse(x));
This will yield a deferred IOrderedEnumerable<string>. The int.Parse is only for the purpose of secondary ordering. If you need to materialize it, ToArray or ToList will need to be called.
Most of the answers here are written using Lambda Syntax, if you want query syntax try this. Result should be the same.
var sortedValues = from x in values
orderby x.Length descending, x descending
select x;
values.OrderByDescending(x => x.Length).ThenByDescending(x => int.Parse(x));

Multiple GroupBy in 1 linq expression

I cannot seem to combine 2 GroupBy statements in 1 linq expression..
For now i'm doing something like this:
double maxInvestment = 0;
foreach (var playerAction in TotalUserActions.GroupBy(p => p.Player))
{
var MaxInvestmentPerPlayer = playerAction.GroupBy(p => p.RoundId)
.Select(p => p.LastOrDefault())
.Sum(p=> p.BetSize);
if(MaxInvestmentPerPlayer > maxInvestment)
maxInvestment = MaxInvestmentPerPlayer;
}
What I would like to do is something like this...
double maxInvestment = TotalUserActions.GroupBy(p => p.Player)
.GroupBy(p => p.RoundId)
.Select(p => p.LastOrDefault())
.Sum(p=> p.BetSize);
But that wont work.. Can someone help me on this?
Thanks!
Looks like this is what you want, the key takeaway being the inner query is wrapped in an outer call to Select():
var maxInvestment = TotalUserActions.GroupBy(p => p.Player)
.Select(g => g.GroupBy(x => x.RoundId)
.Select(x => x.LastOrDefault())
.Sum(x => x.BetSize))
.Max();
I do question your use of LastOrDefault() though as, since you have not specified any ordering, you may as well use FirstOrDefault() and save the hassle of skipping to the last element.

GroupBy and Sum

I read a lot of GroupBy + Sum topics but I didn't understand how to use it.
I have a list of contacts, and in this list, i want to get the state (which appears more).
So my code is:
contacts.GroupBy(i => i.Address.State.ToUpperInvariant());
In this GroupBy, I want to know the state that appears more (and remove the case of "" because empty state is not important to me).
How do I do it?
I was thinking in something like this:
contacts.GroupBy(i => i.Address.State.ToUpperInvariant()).Select(i => i.Max());
Thanks in advance!
You want something like:
var counts = contacts
.Where(c => c.State != string.Empty)
.GroupBy(i => i.Address.State, StringComparer.OrdinalIgnoreCase)
.Select(grp => new { State = grp.Key, Count = grp.Count());
GroupBy returns an IEnumerable<IGrouping<TKey, TSource>>. Since IGrouping<TKey, TSource> implements IEnumerable<TSource>, you can use the Count extension method to get the number of elements in the group.

Why does .Equals not work in this LINQ example?

Why does this yield an empty set?
Object[] types = {23, 234, "hello", "test", true, 23};
var newTypes = types.Select(x => x.GetType().Name)
.Where(x => x.GetType().Name.Equals("Int32"))
.OrderBy(x => x);
newTypes.Dump();
When you do your select you're getting an IEnumerable<String>. Then you're taking the types of each string in the list (which is all "String") and filtering them out where they aren't equal to "Int32" (which is the entire list). Ergo...the list is empty.
Equals works just fine, it's your query that isn't correct. If you want to select the integers in the list use:
var newTypes = types.Where( x => x.GetType().Name.Equals("Int32") )
.OrderBy( x => x );
Reverse the order of the operations:
var newTypes = types.Where(x => x is int)
.OrderBy(x => x)
.Select(x => x.GetType().Name);
(Notice this also uses a direct type check instead of the rather peculiar .GetType().Name.Equals(…)).
The thing with LINQ is you've got to stop thinking in SQL terms. In SQL we think like this:-
SELECT Stuff
FROM StufF
WHERE Stuff
ORDER BY Stuff
That is what your code looks like. However in LINQ we need to think like this :-
FROM Stuff
WHERE Stuff
SELECT Stuff
ORDER BY Stuff
var newTypes = types.Select(x => x.GetType().Name)
.Where(x => x.Equals("Int32"))
.OrderBy(x => x);
This doesn't work because the Select statement will convert every value in the collection to the name of the underlying type of that value. The resulting collection will contain only string values and hence they won't ever have the name Int32.

Categories