States have cities. I need the state with most cities only if there is no tie. Tie means top 2 states have the same number of cities.
var stateWithMostCities = _states
.OrderByDescending(_p => _p.cities.Count())
.Take(2)
.ToList();
Now I can check if the city count of first state = second state and determine if there is a tie. However iam asking if this can achieved on the same line shown above using takewhile, skip and other creative uses of linq. Thanks
Something like this?
var stateWithMostCitiesWithoutATie =_states.GroupBy(_p => _p.cities.Count())
.OrderByDescending(g=>g.Key)
.FirstOrDefault(g=> g.Count()==1? g.First():null);
The key is, as #Mong Zhu pointed out to Group by the counts of cities, after that you can order by desc to get the max, and if the max group has more than one then you have a tie
Technically, you can use Aggregate over ordered states:
// the state with maximum cities; null in case of tie
var stateWithMostCities = _states
.OrderByDescending(state => state.cities.Count())
.Take(2) // at most 2 items to analyze
.Aggregate((s, a) => s.cities.Count() == a.cities.Count() ? null : s);
But I doubt if you should do this: comparing top 2 states is more readable.
Related
I am trying to write a LINQ query that gets all the records and groups them by Period i.e. Sep-18 and then returns the record with the highest Version number within the periods. For example if I have three periods contained within my periodNames list the output list should return:
Sep-18
Versions: 1, 2, 3 (Returns record with version 3)
Oct-18
Versions: 1, 2 (Returns record with version 2)
Nov-18
Versions: 1, 2, 3, 4 (Returns record with version 4)
This is the query I have written so far:
var previousStatements = _context.Statements.Where(x => periodNames.Contains(x.Period) &&
x.Version == _context.Statement.Max(y => y.Version)).toList();
How can I adapt this to the above specification? Thanks
You can use GroupBy in order to group the statements and Max in order to find the maximum value, e.g.
var previousStatements = _context.Statements.Where(x => periodNames.Contains(x.Period))
.GroupBy(x => x.Period)
.Select(x => new { Period = x.Key, MaxVersion = x.Max(y => y.Version))
.ToList();
The code above returns the Period and the maximum version number only. If you need the record with the highest version number for each period, you can use this:
var previousStatements = (ctx.Items.Where(x => periodNames.Contains(x.Period))
.GroupBy(x => x.Period)
.ToArray())
.Select(x => x.OrderByDescending(y => y.Version).First())
.ToList();
Please note that the code above first uses a call to ToArray to send the GroupBy-query to the database. From the returned groups, the row with the highest version number for each period is then retrieved in memory.
Try to use GroupBy and then orderbydescending for the max versiĆ³n:
_context.GroupBy(f => f.Period).Select(f=>f.OrderByDescending(r=>r.Version).First()).ToList();
I think you would have known your solution if you would have written a proper requirement
You wrote:
...groups them by Period i.e. Sep-18 and then returns the highest Version number within the periods
Your examples don't return the highest version number but the row with the highest version number, so let's assume that is what you want:
From a sequence of Statements, group these statements into groups of statements with equal Period, and return from every group, the statement with the largest VersionNumber.
You haven't defined what you want if two statements within the same Period have the same VersionNumber. Let's assume you think that this will not occur, so you don't care which one is returned in that case.
So you have sequence of Statements, where every Statement has a Period and a VersionNumber.
Officially you haven't defined the class of Period and VersionNumber, the only thing we know about them is that you have some code that can decide whether two Periods are equal, and you have something where you can decide which VersionNumber is larger.
IEqualityComparer<Period> periodComparer = ...
IComparer<VersionNumber> versionComparer = ...
If Period is similar to a DateTime and VersionNumber is similar to an int, then these comparers are easy, otherwise you'll need to write comparers.
From your requirement the code is simple:
Take all input statements
Make groups of statements with equal Period
From every group of statements with this Period keep only the one with the highest VersionNumber
IEnumerable<Statement> statements = ...
var latestStatementsWithinAPeriod = statements
.GroupBy(statement => statement.Period, // group by same value for Period
(period, statementsWithThisPeriod) =>
// From every group of statements keep only the one with the highest VersionNumber
// = order by VersionNumber and take the first
statementWithThisPeriod
.OrderByDescending(statement => statement.VersionNumber,
versionComparer)
.FirstOrDefault(),
periodComparer);
Once again: if default comparers can be used to decide when two Periods are equal and which VersionNumber is larger, you don't need to add the comparers.
The disadvantage of the SorBy is that the 3rd and 4rd element etc are also sorted, while you only need the first element, which is the one with the largest VersionNumber.
This can be optimized by using the less commonly used Aggregate:
(period, statementsWithThisPeriod) => statementWithThisPeriod.Aggregate(
(newestStatement, nextStatement) =>
(versionComparer.Compare(newestStatement.VersionNumber, nextStatement.VersionNumber) >=0 ) ?
newestStatement :
nextStatement)
This will put the first statement as the newestStatement (= until now this was the one with the highest version number). The 2nd element will be put in nextStatement. both statements will be compared, and if nextStatement has a VersionNumber larger than newestStatement, then nextStatement will be considered to be newer, and thus will replace newestStatement. The end of the Aggregate will return newestStatement
You can try with GroupBy and OrderByDescending and then take first one.
var statements = _context.Statements
.Where(x => periodNames.Contains(x.Period))
.GroupBy(g => g.Period)
.Select(s => s.OrderByDescending(o => o.Version)
.FirstOrDefault()).ToList();
i'm trying to get data from a db, but I don't get the expected result.
var totalNews = GetNewsData(false, CmsPagesRepository.CurrentUserSettings.CmsLanguageID)
.OrderBy(n => n.Priority)
.ThenBy(n => n.Index)
.ThenBy(n => n.Title);
I have a table of News with a column Index and a column Priority, and I want to order the news by Priority and if the Priority is null first show the ones with priority and after the others.
But now if a have 3 news with index (1,4,2) and priority(null,0,1) in the list of totalNews I get on the first position the one with Priority null and index 1. What do I have to correct?
Though the answer you have accepted will work, I don't much like it. First, in the unlikely event that you have some of the largest integer in there, they will not be ordered correctly with respect to null. A good solution works for any inputs, not just common inputs. Second, the code does not match the specification. Your specification is "order first by whether the priority is null, then by priority, then by...", so that's how the code should read. I would suggest you write:
GetNewsData(...)
.OrderBy(n => n.Priority == null) // nulls last
.ThenBy(n => n.Priority)
.ThenBy(n => n.Index)
.ThenBy(n => n.Title);
You probably want a simple null check in the OrderBy priority, like this:
.OrderBy(n => n.Priority ?? int.MaxValue)
This will default the priority to a high number if it is null.
Background: I have two Collections of different types of objects with different name properties (both strings). Objects in Collection1 have a field called Name, objects in Collection2 have a field called Field.
I needed to compare these 2 properties, and get items from Collection1 where there is not a match in Collection2 based on that string property (Collection1 will always have a greater or equal number of items. All items should have a matching item by Name/Field in Collection2 when finished).
The question: I've found answers using Lists and they have helped me a little(for what it's worth, I'm using Collections). I did find this answer which appears to be working for me, however I would like to convert what I've done from query syntax (if that's what it's called?) to a LINQ query. See below:
//Query for results. This code is what I'm specifically trying to convert.
var result = (from item in Collection1
where !Collection2.Any(x => x.ColumnName == item.FieldName)
select item).ToList();
//** Remove items in result from Collection1**
//...
I'm really not at all familiar with either syntax (working on it), but I think I generally understand what this is doing. I'm struggling trying to convert this to LINQ syntax though and I'd like to learn both of these options rather than some sort of nested loop.
End goal after I remove the query results from Collection1: Collection1.Count == Collection2 and the following is true for each item in the collection: ItemFromCollection1.Name == SomeItemFromCollection2.Field (if that makes sense...)
You can convert this to LINQ methods like this:
var result = Collection1.Where(item => !Collection2.Any(x => x.ColumnName == item.FieldName))
.ToList();
Your first query is the opposite of what you asked for. It's finding records that don't have an equivalent. The following will return all records in Collection1 where there is an equivalent:
var results=Collection1.Where(c1=>!Collection2.Any(c2=>c2.Field==c1.Name));
Please note that this isn't the fastest approach, especially if there is a large number of records in collection2. You can find ways of speeding it up through HashSets or Lookups.
if you want to get a list of non duplicate values to be retained then do the following.
List<string> listNonDup = new List<String>{"6","1","2","4","6","5","1"};
var singles = listNonDup.GroupBy(n => n)
.Where(g => g.Count() == 1)
.Select(g => g.Key).ToList();
Yields: 2, 4, 5
if you want a list of all the duplicate values then you can do the opposite
var duplicatesxx = listNonDup.GroupBy(s => s)
.SelectMany(g => g.Skip(1)).ToList();
I have a generic List List[int, myClass], and I would like to find the smallest int value, and retrieve the items from the list that match this.
I am generating this from another LINQ statement
var traysWithExtraAisles = (from t in poolTrays
where t.TrayItems.Select(i=>i.Aisle)
.Any(a=> ! selectedAisles.Contains(a))
select new
{
count= t.TrayItems.Select(i=>i.Aisle)
.Count(a=> !selectedAisles.Contains(a)),
tray=t
}).ToList();
this gives me my anonymous List of [count, Tray], but now I want to figure out the smallest count, and return a sublist for all the counts that match this.
Can anyone help me out with this?
var smallestGroup = traysWithExtraAisles
.GroupBy(x => x.count)
.OrderBy(g => g.Key)
.First();
foreach(var x in smallestGroup)
{
var poolTray = x.tray;
}
You can use SelectMany to "flatten" your list. Meaning, combine all of the lists into one, then take the Min. So;
int minimum = poolTrays.SelectMany(x => x).Min(x => x.TheIntegerIWantMinOf);
Will give you the smallest value contained in the sub lists. I'm not entirely sure this is what you're asking for but if your goal is simply to find the smallest element in the collection then I would scrap the code you posted and use this instead.
Right, I now realise this is actually incredibly easy to do with a bit more fiddling around. I have gone with
int minCount = traysWithExtraAisles.Min(x=>x.count);
var minAislesList = (from t in trayswithExtraAisles
where t.count==mincount
select t).ToList()
I imagine it is probably possible to do this in one statement
You can use GroupBy as answered by Tim... or OrderBy as follow:
var result = traysWithExtraAisles.OrderBy(x=>x.count)
.TakeWhile((x,i)=> i == 0 || x.count == traysWithExtraAisles[i-1]).count;
I have an IEnumerable of invoices, these invoices have line items. These line items have a priority. I'm programming a variety of strategies to automatically apply cash against these line items and one is giving me some trouble. My pattern has been to prepare a linq statement to order the line items of the invoices then iterate over the linq query applying cash in order until I run out.
An example of this linq statement for the simplest strategy, pay each line item by priority and due date, is shown below:
from lineItem in invoices.SelectMany(invoice => invoice.LineItems)
orderby lineItem.Priority, lineItem.DueDate
select lineItem;
One of the strategies is to apply cash to the oldest remaining item with a given priority, in priority order, then move to the next oldest of each priority.
EDIT: Example of how one might start the query I'm asking for -
from lineItem in invoices.SelectMany(invoice => invoice.LineItems)
group lineItem by lineItem.Priority into priorities
orderby priorities.Key
select priorities.OrderBy(item => item.DueDate);
We now have "buckets" of line items with the same priority, ordered by due date within the bucket. I need to extract the first line item from each bucket, followed by the second, etc. until I have ordered all of the items. I would like to perform this ordering purely in linq.
Can anyone think of a way to express this entirely in linq?
I don't see how you'll get this down to a better query than what you have, perhaps nest from queries to automatically do the SelectMany.
var proposedPayments = new List<LineItem>();
decimal cashOnHand = ...;
var query = invoices.SelectMany(iv => iv.LineItems)
.GroupBy(li => li.Priority)
.SelectMany(gg =>
gg.OrderBy(li => li.DueDate)
.Select((li,idx) => Tuple.Create(idx, gg.Key, li)))
.OrderBy(tt => tt.Item1)
.ThenBy(tt => tt.Item2)
.Select(tt => tt.Item3);
foreach (var item in query)
{
if (cashOnHand >= item.Cost)
{
proposedPayments.Add(item);
cashOnHand -= item.Cost;
}
if (cashOnHand == 0m) break;
}
Edit: updated to match the paragraph the author wanted. Selected as first of each priority.
LINQ = Language Integrated QUERY not Language Integrated PROCEDURAL CODE.
If you want a query that returns the line items you need to apply the payment to, then that's do-able (see .Aggregate), but if you want to actually apply the money to the line items as you go, then a foreach loop is a fine construct to use.
See http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx