GroupBy loses ThenBy sort order - linq

How do I build an expression that will preserve the ThenBy order?
The Expression below produces the Groups ordered by Bowler.Number but the Matches of each group are not sorted.
public class Match
{
public int Id { get; set; }
public virtual Bowler Bowler { get; set; }
public byte GameNumber { get; set; }
public int Score { get; set; }
...
}
public class Bowler
{
public int Id { get; set; }
public int Number { get; set;}
...
}
var GroupedMatches = db.Matches.OrderBy(m=>m.Bowler.Number).ThenBy(m=>m.GameNumber).GroupBy(m => m.Bowler.Number)
Here is the output I want:
1
game1 295
game2 199
game3 202
game4 178
2
game1 177
...
Current I use two foreach like
foreach (var item in TheGroups){
... do some stuff with the Group
foreach (var x in item.OrderBy(a =>a.Number)) { //<-- can I get rid of this OrderBy?
... do some stuff with the Matches in this group
}
}
No big deal, I just thought LINQ would be able to help me sort the matches in the group when the groups where built, not later when I process them.

I'm not sure what you want the output to be, since it's kind of non-sensical to order a list of groups keyed on bowler number by game number.
Assuming you want an ordered list of bowler numbers, each containing an ordered list of games, something like this might work
var GroupedMatches = db.Matches
.GroupBy(m => m.Bowler.Number)
.OrderBy(g => g.Key)
.Select (g => new
{
BowlerNumber = g.Key,
Matches = g.OrderBy(m => m.GameNumber).ToList()
});
Here's specifically how you get the output you're looking for
foreach(var group in GroupedMatches) {
Console.WriteLine(group.BowlerNumber);
foreach(var match in group.Matches) {
Console.WriteLine("game{0} {1}", match.GameNumber, match.Score);
}
}

Related

LINQ Lazy load or query incorrect

LINQ Query not populating
Model extract is as follows
public class ServiceBulletin
{
[Key]
public int Id { get; set; }
public virtual ICollection<ServiceBulletinProducts> ApplicableProducts { get; set; }
}
public class ServiceBulletinProducts
{
[Key]
public int Id { get; set; }
public int ServiceBulletinId { get; set; }
public virtual Product Product{ get; set; }
}
I'm using the following code at the moment to populate a collection
var x = from m in _dc.ServiceBulletins.Include(p => p.ApplicableProducts)
.Include(m => m.Manufacturer)
where m.DeleteStatus == DeleteStatus.Active
select m;
var x1 = new List<ServiceBulletin>();
foreach (var item in x)
{
var p = from m1 in _dc.ServiceBulletinsProducts.Include(p2=>p2.Product)
where m1.Product.DeleteStatus == DeleteStatus.Active &&
m1.ServiceBulletinId == item.Id
select m1;
var p99 = p.ToList();
item.ApplicableProducts = p99;
x1.Add(item);
};
So this is intended to have a Parent Child relationship and I’m trying to do a query which populates a collection of ServiceBulletins with a ApplicableProducts item with a fully populated collection of ServiceBulletinProducts for the ServiceBulletin with the values of the Product populated
The collection is populated but the ServiceBulletinProducts are always set to null and I can’t seem to add an Include such as .Include(p => p.ApplicableProducts.Products) to try and populate the product details – which is resulting in me iterating around the collection to populate the items.
Am I missing something to enable the population on the 1st query for the Include statement or do I need to do the query in a different way ?
Figured out the following should do the trick.
var x = from m in _dc.ServiceBulletins.Include(p => p.ApplicableProducts.Select(p2=>p2.Product))
.Include(m => m.Manufacturer)
where m.DeleteStatus == DeleteStatus.Active
select m;

IQueryable to get sum and order by descending in subset

I have a collection of DTO's in which I want to do some ordering by the sum of some values in a nested DTO.
The hiearchy is as follows:
Its a collction of QuestionDTO's.
A QuestionDTO has Many Answers which has Many Votes.
So in short:
1 QuestionDto: QuestionID, QuestionTitle, ANSWERS: AnswerId, Answer, VOTES: VoteId, AnswerId, Value
Its the last value that I want to have summed for each answer, and then order by this sum for each question. Making the most popular question/answer on top of the list.
Thanks in advance
I think you want this:
var questionList = new List<Question>();//Get a real list
var sortedQuestions = (from i in questionList
select new
{
i.QuestionID,
i.QuestionText,
VotesSum = i.Answers.Sum(ee => ee.Votes.Sum(ss => ss.Value))
}
).OrderByDescending(ee => ee.VotesSum);
foreach (var item in sortedQuestions)
Console.WriteLine(item.QuestionText + " " + item.VotesSum);
Asumming your classes are like these
class Vote
{
public int VoteID { get; set; }
public int Value { get; set; }
}
class Answer
{
public int AnswerID { get; set; }
public string AnswerText { get; set; }
public List<Vote> Votes = new List<Vote>();
}
class Question
{
public int QuestionID { get; set; }
public string QuestionText { get; set; }
public List<Answer> Answers = new List<Answer>();
}
Here's a way to get what you want:
var query = questions.Select(q =>
new { q.QuestionText,
Answers = q.Answers.Select(a =>
new { a.AnswerText,
Votes = a.Votes.Sum(v => v.Value)
})
})
.Select(q =>
new { q.QuestionText,
Votes = q.Answers.Sum(a => a.Votes),
Answers = q.Answers.OrderByDescending(a => a.Votes)
})
.OrderByDescending(q => q.Votes);
As you see, the votes are summed at two levels, Answer and Question and sorted in descending order.

Linq Query Need - Looking for a pattern of data

Say I have a collection of the following simple class:
public class MyEntity
{
public string SubId { get; set; }
public System.DateTime ApplicationTime { get; set; }
public double? ThicknessMicrons { get; set; }
}
I need to search through the entire collection looking for 5 consecutive (not 5 total, but 5 consecutive) entities that have a null ThicknessMicrons value. Consecutiveness will be based on the ApplicationTime property. The collection will be sorted on that property.
How can I do this in a Linq query?
You can write your own extension method pretty easily:
public static IEnumerable<IEnumerable<T>> FindSequences<T>(this IEnumerable<T> sequence, Predicate<T> selector, int size)
{
List<T> curSequence = new List<T>();
foreach (T item in sequence)
{
// Check if this item matches the condition
if (selector(item))
{
// It does, so store it
curSequence.Add(item);
// Check if the list size has met the desired size
if (curSequence.Count == size)
{
// It did, so yield that list, and reset
yield return curSequence;
curSequence = new List<T>();
}
}
else
{
// No match, so reset the list
curSequence = new List<T>();
}
}
}
Now you can just say:
var groupsOfFive = entities.OrderBy(x => x.ApplicationTime)
.FindSequences(x => x.ThicknessMicrons == null, 5);
Note that this will return all sub-sequences of length 5. You can test for the existence of one like so:
bool isFiveSubsequence = groupsOfFive.Any();
Another important note is that if you have 9 consecutive matches, only one sub-sequence will be located.

Append OR subquery in Linq

I am trying to build a simple search against some entities (EF4, if that makes any difference). Passed into my search query is a list of criteria objects. The crieteria object looks like this:
public class ClaimSearchCirtieria
{
public Guid? FinancialYear { get; set; }
public bool AllClaimants { get; set; }
public IList<Guid> ClaimantIds { get; set; }
public bool AllExpenseCategories { get; set; }
public IList<ExpenseCategoryAndTypeCriteria> EpenseCategoryAndTypes { get; set; }
}
And the ExpenseCategoryAndTypeCriteria
public class ExpenseCategoryAndTypeCriteria
{
public Guid ExpenseCategory { get; set; }
public bool AllTypesInCatgeory { get; set; }
public IList<Guid> ExpenseTypes { get; set; }
}
Searching on financial years and claimants needs to be an AND query, then I need the expense categories and expense types to be appended as an OR sub query.
In essence I'm trying to do:
select *
from claims
where <financial year> AND <Claimants> AND (expense type 1 OR expense type 2 or expense category X)
So far I've got this:
public PagedSearchResult<Claim> Search(ClaimSearchCirtieria searchCriteria, int page, int pageSize)
{
var query = All();
if (searchCriteria.FinancialYear.HasValue)
{
query = from claim in query
where claim.FinancialYearId == searchCriteria.FinancialYear
select claim;
}
if (!searchCriteria.AllClaimants)
{
query = from claim in query
where searchCriteria.ClaimantIds.Contains(claim.ClaimantId)
select claim;
}
if (!searchCriteria.AllExpenseCategories)
{
foreach (var item in searchCriteria.EpenseCategoryAndTypes)
{
if (item.AllTypesInCatgeory)
{
//Just search on the category
query = query.Where(claim =>
(from transaction in claim.ClaimTransactions
where item.ExpenseCategory == transaction.ExpenseType.ExpenseCategoryId
select transaction).Count() > 0
);
}
else
{
//Search for the specified types
query = query.Where(claim =>
(from transaction in claim.ClaimTransactions
where item.ExpenseTypes.Contains(transaction.ExpenseTypeId)
select transaction).Count() > 0
);
}
}
}
return PagedSearchResult<Claim>.Build(query, pageSize, page);
}
What I'm currently seeing is that the last expense category requested is the only expense category I get results for. Also, looking at the code, it looks like I would expect this to be building a series of AND queries, rather that the required OR.
Any pointers?
You can do this with LINQKit's PredicateBuilder. You need to use AsExpandable() when composing Entity Framework queries.

linq aggregate

class Category
{
public string Name { get; set; }
public int Count { get; set;}
}
Name Count
AA 2
BB 3
AA 4
I have an IEnumerable<Category>
and would like to get a list of Categories with unique names and the sum of multiple entries
Output
Name Count
AA 6
BB 3
Update
class Category
{
public string Name { get; set; }
public int CountA { get; set;}
public int CountB { get; set;}
public string Phone { get; set;}
}
How would I sum two columns. and the phone column can be the last row or any row
Your updated question isn't entirely clear in terms of the phone number, but I suspect you want something like:
var query = from category in list
group category by category.Name into grouped
select new { Name = grouped.Key,
SumA = grouped.Sum(x => x.CountA),
SumB = grouped.Sum(x => x.CountB),
Phone = grouped.Last().Phone };
Changing grouped.Last() to grouped.First() would be more efficient, by the way.
Evaluating multiple aggregates in this way isn't terribly efficient in general. The Push LINQ project developed by myself and Marc Gravell makes it a lot more efficient at the cost of not being quite as easy to use. You might want to look into it if you need to deal with a lot of data.
var foo = new List<Category>() {
new Category() { Name = "AA", Count = 2},
new Category() { Name = "BB", Count = 3},
new Category() { Name = "AA", Count = 4}
};
var bar = foo.GroupBy(c => c.Name).Select(g => new Category(){ Name = g.Key, Count = g.Sum(c => c.Count) });

Resources