Get first item from each category - c#

I have 5 categories:
Book, DVD, Rent, Buy, Sell
I use the following query to select the latest count of items.
public async Task<IList<Post>> GetRecentPostsAsync(int count)
{
return await _db.Posts
.Where(e => e.Published.HasValue && e.Published.Value <= DateTime.Today)
.OrderByDescending(e => e.Published)
.Take(count)
.ToListAsync();
}
My Post class looks like this:
public class Post
{
#region Properties
[Key]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
[Required]
[StringLength(25)]
public string Category { get; set; }
public DateTime? Published { get; set; }
#endregion
}
This returns my count of items. I want to make a new function where I get the most recently published item from the first 4 categories, so excluding the category Sell. It should return 4 items back.
How can I achieve this?

This should give you back a List<Post> with the last published Post in each category except "Sell":
return await _db.Posts
.Where(x => x.Category != "Sell" && x.Published.HasValue && x.Published.Value <= DateTime.Today)
.GroupBy(x => x.Category)
.Select(x => x.OrderByDescending(x => x.Published).Take(1))
.SelectMany(x => x)
.ToListAsync();

Didn't immediately understand the question. So, try that.
public async Task<IList<Post>> GetRecentPostsAsync()
{
List<string> categories = new List<string> { "Book", "DVD", "Rent", "Buy" };
List<Post> result = new List<Post>();
foreach (string category in categories)
{
Post post = db.Posts.Where(e => e.Published.HasValue && e.Published.Value <= DateTime.Today && e.Category == category)
.OrderByDescending(e => e.Published)
.Take(1).First();
result.Add(post);
}
return result;
}

Try this. This will give you the count by Category excluding Sell
public class PostCategory
{
public string Category { get; set; }
public int Count { get; set; }
}
public async Task<IList<PostCategory>> GetRecentPostsAsync()
{
return await _db.Posts
.Where(e => e.Published.HasValue && e.Published.Value <= DateTime.Today && e.Category != "Sell")
.GroupBy(x => x.Category)
.Select(y => new { Category = y.Key, Count = y.Count() })
.ToListAsync();
}

Related

Select First of Each Navigation Property

I have three classes that look like the following. Basically, one order will be assigned multiple status.
public class Order {
public int Id { get; set; }
public DateTime Created { get; set; }
...
public ICollection<OrderStatus> OrderStatus { get; set; }
}
public class Status {
public int Id { get; set; }
public DateTime Date { get; set; }
...
public ICollection<OrderStatus> OrderStatus { get; set; }
}
public class OrderStatus {
public int Id { get; set; }
public Order Order{ get; set; }
public Status Status { get; set; }
}
My goal is to get the last status of every single order that has been assigned to a status between 0001-1000. This is what I have so far:
orders.SelectMany(x => x.OrderStatus.Where(x => x.Status.Id >= 0001 && x.Status.Id <= 1000).OrderByDescending(x => x.Date).Select(x => x.Date));
This part is where I only want to select the first of each and not all of them.
.Select(x => x.Date)
Edit:
I got it working, with the help of #StriplingWarrior!
I forgot to mention, that I only want the date of the last status per order. So basically, I will have a list of dates at the end for each order. This is the final product:
List<DateTime> dates = buchungen.SelectMany(x => x.OrderStatus.Where(x => x.Status.Id >= 0001 && x.Status.Id <= 1000).GroupBy(x => x.OrderId).Select(g => g.OrderByDescending(x => x.Date).FirstOrDefault()).Select(x => x.Date));
If you want the order information along with its last status, this is the general structure to use:
orders
.Select(x => new
{
order = x,
lastStatus = x.OrderStatus
.Where(x => x.Status.Id >= 0001 && x.Status.Id <= 1000)
.OrderByDescending(x => x.Date)
.FirstOrDefault()
})
If you literally only want the last status per order, you can probably go straight to the order status table, something like this:
context.OrderStatuses
.Where(s => s.Id >= 0001 && s.Id <= 1000)
.GroupBy(s => s.OrderId)
.Select(g => g.OrderByDescending(x => x.Date).FirstOrDefault())

LINQ how to Filter the data based on the value of the properties

i have a problem with filtering data in LINQ , here is my Model :
public class CoursePlan
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Semester { get; set; }
public string ModuleCode { get; set; }
public string ModuleName { get; set; }
public string Credits { get; set; }
public string OrderNumber { get; set; }
public string ModuleStatus { get; set; }
}
and here is my data Json
the problem here some modules having same OrderNumber which mean they are optional , student must study one of them and if student already study one of them , i should ignore other modules in same order number.
in other way to describe the question
i want to return a list of CoursePlan and on this list if there is two items having same OrderNumber check the ModuleStatus for each one of them and if any one is Completed remove other modules on that order otherwise return them all .
here is my code
var coursePlanList = await _sqLiteAsyncConnection.Table<CoursePlan>().ToListAsync();
var groupedData = coursePlanList.OrderBy(e => e.Semester)
.GroupBy(e => e.OrderNumber)
.Select(e => new ObservableGroupCollection<string, CoursePlan>(e))
.ToList();
for now im solving this by this algorithm and not sure if it's the best
var coursePlanList = await _sqLiteAsyncConnection.Table<CoursePlan>().ToListAsync();
List<CoursePlan> finalList = new List<CoursePlan>();
var counter = 0;
foreach (var itemPlan in coursePlanList)
{
if (counter > 0 && counter < coursePlanList.Count)
if (itemPlan.OrderNumber == coursePlanList[counter - 1].OrderNumber)
{
if (itemPlan.ModuleStatus == "Completed")
{
finalList.RemoveAll(a => a.OrderNumber == itemPlan.OrderNumber);
finalList.Add(itemPlan);
}
Debug.WriteLine(itemPlan.ModuleName + "With -->" + coursePlanList[counter - 1].ModuleName);
}
else
finalList.Add(itemPlan);
counter++;
}
var groupedData = finalList.OrderBy(e => e.ModuleStatus)
.ThenBy(e => e.Semester)
.GroupBy(e => e.Semester)
.Select(e => e)
.ToList();
CoursePlanViewList.BindingContext = new ObservableCollection<IGrouping<string, CoursePlan>>(groupedData);
Any advise or guidance would be greatly appreciated
Let me rephrase your requirement: you want to show all plans per OrderNumber that meet the condition: none of the plans in their group should be "Completed" or the plans themselves should be "Completed". All this grouped by Semester:
var plansQuery =
from p in _sqLiteAsyncConnection.Table<CoursePlan>()
group p by p.Semester into sem
select new
{
PlansInSemester =
from p in sem
group p by p.OrderNumber into gp
select new
{
PlansInOrderNumber =
gp.Where(p => !gp.Any(p1 => p1.ModuleStatus == "Completed")
|| p.ModuleStatus == "Completed")
}
};
This gives you an IQueryable that produces the course plans you want to select, but grouped in two levels, so the final result is obtained by flattening the query twice:
var coursePlanList = await plansQuery
.SelectMany(x => x.PlansInSemester
.SelectMany(y => y.PlansInOrderNumber)).ToListAsync()

Why does calling .Include() after a .Join() not work?

I have a DB structure which is not ideal, but I have coded it into EF like this:
[Table("Item")]
public class Item
{
[Key] public int Id { get; set; }
public int CategoryId { get; set; }
public int ItemTypeId { get; set; } // An ItemTypeId of 1 means this row refers to an Article
public int ItemId { get; set; } // this refers to the Article primary key
}
[Table("Article")]
public class Article
{
[Key] public int Id { get; set; }
...
public virtual ICollection<SubArticle> SubArticles { get; set; }
}
[Table("SubArticle")]
public class SubArticle
{
...
public int ArticleId { get; set; }
}
modelBuilder.Entity<Article>().Collection(_ => _.SubArticles).InverseReference(_ => _.Article).ForeignKey(_ => _.ArticleId);
What I want to do is get all articles (with the corresponding sub-articles) that belong to a specific category. I have this query which is working:
var result = await Context.Set<Item>()
.Where(i => i.CategoryId == 200)
.Where(i => i.ItemTypeId == 1)
.Join(
Context.Set<Article>().Include(a => a.SubArticles),
i => i.ItemId,
a => a.Id,
(i,a) => a)
.ToListAsync();
result.SubArticles.First(); // works
My question is why does this query not work:
var result = await Context.Set<Item>()
.Where(i => i.CategoryId == 200)
.Where(i => i.ItemTypeId == 1)
.Join(
Context.Set<Article>(),
i => i.ItemId,
a => a.Id,
(i,a) => a)
.Include(a => a.SubArticles)
.ToListAsync();
result.SubArticles.First(); // error: result.SubArticles is null

Select common value in navigation table using LINQ lambda expression

I have a table called InvestigatorGroup and a table called InvestigatorGroupUsers which is used to see what groups have what users. I am trying to get the common investigator group between two users
My query is as follows:
public InvestigatorGroup GetCommonGroup(string userId, string investigatorUserId)
{
using (GameDbContext entityContext = new GameDbContext())
{
string[] ids = new[] { userId, investigatorUserId };
return entityContext.InvestigatorGroups
.Where(i => i.IsTrashed == false)
.Include(i => i.InvestigatorGroupUsers)
.Where(i => i.InvestigatorGroupUsers.Any(e => ids.Contains(e.UserId)))
.OrderByDescending(i => i.InvestigatorGroupId)
.GroupBy(i => i.InvestigatorGroupId)
.Where(i => i.Count() > 1)
.SelectMany(group => group).FirstOrDefault();
}
}
The entity InvestigatorGroup is as follows:
public class InvestigatorGroup : IIdentifiableEntity
{
public InvestigatorGroup()
{
this.InvestigatorGroupGames = new HashSet<InvestigatorGroupGame>();
this.InvestigatorGroupUsers = new HashSet<InvestigatorGroupUser>();
}
// Primary key
public int InvestigatorGroupId { get; set; }
public string InvestigatorGroupName { get; set; }
public bool HasGameAssignment { get; set; }
public string GroupRoleName { get; set; }
public bool IsTrashed { get; set; }
// Navigation property
public virtual ICollection<InvestigatorGroupUser> InvestigatorGroupUsers { get; private set; }
public virtual ICollection<InvestigatorGroupGame> InvestigatorGroupGames { get; private set; }
public int EntityId
{
get { return InvestigatorGroupId; }
set { InvestigatorGroupId = value; }
}
}
The problem is that it keeps returning a value of 0. It doesn't see the shared group with a count of 2 between the two users.
I did a test to return the groups (I removed the count>1 condition) and it returned all the groups for both users not only the one they have in common
I believe the issue is with this line: .Where(i => i.InvestigatorGroupUsers.Any(e => ids.Contains(e.UserId)))
Thanks for the help!
I've resolved this by changing my query so that it searches for the rows containing one of the UserId's. Then it queries through those selected rows and selects the ones containing the other UserId (InvestigatorUserId). This way only the rows containing both are returned
My new code is as follows:
public InvestigatorGroup GetCommonGroup(string userId, string investigatorUserId)
{
using (GameDbContext entityContext = new GameDbContext())
{
IEnumerable<InvestigatorGroup> userGroups = entityContext.InvestigatorGroups
.Where(i => i.IsTrashed == false)
.Include(i => i.InvestigatorGroupUsers)
.Where(i => i.InvestigatorGroupUsers.Any(e => e.UserId.Contains(userId)))
.OrderByDescending(i => i.InvestigatorGroupId);
return userGroups.Where(i => i.InvestigatorGroupUsers.Any(e => e.UserId.Contains(investigatorUserId))).FirstOrDefault();
}
}

Order by property of nested object with NHibernate Linq

I have class Request with list of Comments. Each Request can have zero, one or many Comments.
public class Request
{
public virtual string Id
{ get; protected set; }
public virtual DateTime Date
{ get; set; }
public virtual byte RequestStatusId
{ get; set; }
public virtual Payment Payment
{ get; set; }
public virtual IList<RequestComment> RequestComments
{ get; set; }
}
public class RequestComment
{
public virtual int Id
{ get; protected set; }
public virtual DateTime Date
{ get; set; }
public virtual string CommentText
{ get; set; }
public virtual Request Request
{ get; set; }
public virtual User User
{ get; set; }
}
I'm using NHibernate.Linq to get data from database.
When I sort, for example by Request Id, it looks like this:
var query = _session.Query<Request>()
.Where(r => r.RequestStatusId == requestStatusId)
.OrderBy(r => r.Id)
.Skip(pageNo * pageSize)
.Take(pageSize);
return query.ToFuture().AsQueryable();
When I need last comment for Request I get it like this:
public RequestComment GetLastCommentForRequest(string requestId)
{
var query = _session.Query<RequestComment>()
.Where(r => r.Request.Id == requestId)
.OrderByDescending(r => r.Date)
.FirstOrDefault();
return query;
}
Now I need to get Requests, with their last comment (if exists) and all sorted by CommentText. I was trying to do order in Request query with:
.OrderBy(x => x.RequestComments.Where(y => y.Request.Id == x.Id).OrderByDescending(y => y.Date).FirstOrDefault())
But it's not working and I'm getting error »Exception has been thrown by the target of an invocation.«
UPDATE
This is ok, but it's not sorted by last comment, but by first found:
.OrderBy(r => r.RequestComments.Select(x => x.CommentText).FirstOrDefault())
How about this :
.OrderBy(x => x.RequestComments
.OrderByDescending(y => y.Date)
.Select(x => x.CommentText)
.FirstOrDefault()
)
UPDATE :
It seems that linq above translated to query with subquery having order by clause which is not allowed. Try this instead :
.OrderBy(x => x.RequestComments
.Where(y => y.Date == x.RequestComments.Max(o => o.Date))
.Select(u => u.CommentText)
.FirstOrDefault()
)
You could try:
.OrderBy(x => x.RequestComments.Select(y => y.Date).DefaultIfEmpty().Max())

Categories