MVC2 Grouping and displaying products by date - c#

Good morning Stack Overflow, I just passed my view a list of products for the last 90 days sorted by date and need to display it in an efficient way that will group the products by date. Linq-To-Sql doesn't understand a lot of the date comparison functions, so I'm kinda lost as to what to do here.
What I would like to do is contain each group of products with the same date inside of a div with a title like "Today", "Yesterday" or "Last 30 Days". Can anybody assist?

So, as i understand you need groups like history groups in skype:
Show messages from: Yesterday * 7 days * 30 days .. etc.
Suppose you have class Product with Date and some others fields, like this:
public class Product
{
public DateTime Date { get; set; }
public string ProductName { get; set; }
}
Than you can create some helper class like this:
public enum ProductGroupEnum
{
Today = 0,
Yesterday = 1,
Last30Days = 30
}
public static class ProductsHelper
{
public static List<Product> GetProductsGroup(ProductGroupEnum group, IEnumerable<Product> products)
{
var daysCount = (int)group;
return products.Where(x => DateTime.Now.Date - x.Date.Date <= new TimeSpan(daysCount, 0, 0, 0)).ToList();
}
}
Instead of enum you can pass date i think, like DateTime.Now.AddDays(-1) for example.
Because if you want 'last 3 month group' it is incorrect to use 90 days.
And a final example of code:
var products = new List<Product>()
{
new Product() {Date = DateTime.Now.AddMinutes(-30), ProductName = "TodayProduct"},
new Product() {Date = DateTime.Now.AddDays(-1), ProductName = "YesteradayProduct"},
new Product() {Date = DateTime.Now.AddDays(-25), ProductName = "LastMonthProduct"}
};
var todayProducts = ProductsHelper.GetProductsGroup(ProductGroupEnum.Today, products);
var yesterdayProducts = ProductsHelper.GetProductsGroup(ProductGroupEnum.Yesterday, products);
So 'todayProducts' will contain only first one products,
but 'yesterdayProducts' will containt first two products(means products from yesterday to today).
Also you can easily use above 'ProductsHelper' helper method in 'view' for products filtering according yous needs.
Hope this help.

Related

Get group of data from EntityCollection with LinkEntity

I have a flat structure of data which I have retrieved with a QueryExpression and LinkEntity (InnerJoin). I have aliased the child with "element"
So my data looks like this:
parentIdPK parentStatus element.ChildIdPK element.parentIdFK
1 100 10 1
1 100 11 1
1 100 12 1
2 100 13 2
2 100 14 2
3 100 15 3
3 100 16 3
3 100 17 3
So bascially I have a Parent/Child structure and I want to push this data into my own classes:
public class ExistingProposal
{
public Guid parentIdPK { get; set; }
public int parentStatus { get; set; }
public List<ExistingElement> Elements { get; } = new List<ExistingElement>();
}
public class ExistingElement
{
public Guid ChildIdPK { get; set; }
public Guid parentIdFK { get; set; }
}
So in general this would lead to have one ExistingProposal with N ExistingGRProposalElement's
Ho can I achieve this in the best way? I have tried with linq but I'm struggling pretty much with this.
What I am trying actually is to group the data with linq:
var groups = from a in result.Entities
orderby a.Attributes["parentId"]
group a by a.Attributes["parentId"] into g
select new { g };
The problem I have actually is I dont know exactly from where to start to create the needed class structure.
Maybe somebody can point me to the right direction?
Any hint is highly appreciated.
Your question isn't very clear, but, if I understood well the following expression will do the trick for you :
var groups = from a in result.Entities
group a by a.Attributes["parentId"] into g
select new ExistingProposal {
parentIdPK = a.Key,
parentStatus = (int)a.FirstOrDefault().Attributes["parentStatus"],
Elements = (from y in g
select new ExistingElement {
ChildIdPK = y.Attributes["element.ChildIdPK"],
parentIdFK = a.Key
}).ToList()
};
You'll need to add a setter to your Elements property in ExistingProposal
You don't need to order before grouping
You should rename intermediate vars (y, g, a, etc.) to more meaningful ones

Getting sum from table and showing into razor view

I have two tables like this
Table 1 : animal
Country Lion Tiger State
india 4 5 Madhya Pradesh
india 10 2 Utrakhand
Russia 0 10 Primorsky krai
Russia 1 20 Khabarovsk Krai
and Table 2: Project_Tiger
Country No
India 10
Russia 5
I have created inumerable class like this
public animal animal {get;set;};
public project_tiger project_tiger {get;set;};
now I want to show result something like this in view
Country NO lion tiger
india 10 14 7
Russia 5 1 30
here lion and tiger is sum of both the record in table 1
10+4= 15, 5+2 =7, for india and same for russia
now I am lacking of knowledge how to query these data as sum from database using linq and how to show this in razor view
I can write query in sql like this but can't translate it to linq
select animal.country, No, sum(lion), sum(tiger) from animal
inner join project_tiger ON animal.country equals project_tiger.country
Any help regarding this will be appreciated.
You basically need to join both the tables and group the results by the country name and generate the result out of that.
var groupd = (from a in dbContext.Animals
join b in dbContext.ProjectTigers on a.Country equals b.Country
select new { Country = a.Country,
No = b.No,
Lion = a.Lion,
Tiger = a.Tiger }
) // We have the join results. Let's group by now
.GroupBy(f => f.Country, d => d,
(key, val) => new { Country = key,
No = val.First().No,
Lion = val.Sum(s => s.Lion),
Tiger = val.Sum(g => g.Tiger) });
This will give you a collection of anonymous objects. If you have a view model/dto to represent your expected data items, you may use that in the projection part of this linq expression.
Also, like others mentioned in the comments, you might want to look into a better way of building your db schema.
You can still acheive it using EntityFramework, and still use the power of the SQL server to preform the lifting for you.
By using directly with the generic method SqlQuery<> this can be done pretty easily.
Create a class that will fit your need
public class AnimalsCount
{
public int No { get; set; }
public int Lion { get; set; }
public int Tiger { get; set; }
}
Now use the Generic method of SqlQuery<AnimalsCount>
var animalsCount = ctx.Database
.SqlQuery<AnimalsCount>("SELECT Country,(SELECT [No] FROM ProjectTiger WHERE Country = a.Country) as [No], sum(lion) as Lion, sum(tiger) as Tiger FROM [Animal] as a GROUP BY Country")
.ToList();

Take with group order by

I have a web app for calculating the results of a competition:
The competitors attempt x number of activities (each activity is assigned a point value) over several hours.
Their total score is the sum of the 5 highest point values.
I have the following code in my controller. I have tried using .Take(5) in several places but it returns either the top 5 scores only, or the first 5 entered in the table.
The grouping is over several fields as the competitors are awarded prizes by Category (age) and by Gender. I am using a viewmodel named "Game". My most recent unsuccessful code block:
var compdata = from result in db.Results
where result.Complete == true
orderby result.Activity.Value descending
group result by new
{
result.CompetitorId,
result.Competitor.Name,
result.Competitor.Category,
result.Competitor.Gender,
}
into resultsGroup
select new Game
{
CompetitorId = resultsGroup.Key.CompetitorId,
Name = resultsGroup.Key.Name,
Category = resultsGroup.Key.Category,
Gender = resultsGroup.Key.Gender,
Score = resultsGroup.Sum(s => s.Activity.Value)
};
I think you're almost there. When working out the Score value, you need do the Take(5) at that point ... after the grouping. The following isn't the most succinct way to do it but it demonstrates the point based on what you have right now:
Score = resultsGroup.OrderByDescending(s => s.Activity.Value).Take(5).Sum(s => s.Activity.Value)
So that gives something similar to:
var compdata = from result in db.Results
where result.Complete == true
group result by new
{
result.CompetitorId,
result.Competitor.Name,
result.Competitor.Category,
result.Competitor.Gender,
}
into resultsGroup
select new Game
{
CompetitorId = resultsGroup.Key.CompetitorId,
Name = resultsGroup.Key.Name,
Category = resultsGroup.Key.Category,
Gender = resultsGroup.Key.Gender,
Score = resultsGroup.OrderByDescending(s => s.Activity.Value).Take(5).Sum(s => s.Activity.Value)
};

How to get the correct date from this grouped data?

I have a dataset with data which has a value every 5 mins. Now I have this group by LINQ query where I group this data in hourly slots.
I want to get the correct Timestamp for each hour, I used n.First().Timestamp here, but from my results it seems wrong. On a dataset with a few years data, I only get 24 points back.
Node looks like this:
public class Node
{
public DateTime Timestamp;
public double Value;
public string NodeName;
}
Can anybody tell me how I can get the right Timestamp of the hourly grouped data? So instead of having a Timestamp like 1/01/2014 14:05:00, 1/01/2014 14:10:00, I'd just have for every hourly grouping 1/01/2014 14:00:00, 1/01/2014 15:00:00.
hourlynodes = (from node in nodedata group node by node.Timestamp.Hour into n select new Node
{ Timestamp = new DateTime(n.First().Timestamp.Year, // How do I get the right Year/Month/Day for each grouped hour???
n.First().Timestamp.Month,
n.First().Timestamp.Day,
n.Key, 0, 0), // n.Key here is the hour grouping
Value = n.Average(x => x.Value),
NodeName = n.First().NodeName
}
).OrderBy(x => x.Timestamp.Date);
You need to SELECT to the nearest hour, not just use that hour as the basis. So you'll need an extension method:
public static DateTime ToNearestHour(this DateTime dt)
{
return dt.Date.AddHours(dt.Hours);
}
Then it's as simple as slightly modifying the group.
hourlynodes = (from node in nodedata group node by node.Timestamp.ToNearestHour() into n select new Node
{ Timestamp = new DateTime(n.First().Timestamp.Year, // How do I get the right Year/Month/Day for each grouped hour???
n.First().Timestamp.Month,
n.First().Timestamp.Day,
n.Key, 0, 0), // n.Key here is the hour grouping
Value = n.Average(x => x.Value),
NodeName = n.First().NodeName
}
).OrderBy(x => x.Timestamp.Date);

Ravendb mapreduce grouping by multiple fields

We have a site that contains streaming video and we want to display three reports of most watched videos in the last week, month and year (a rolling window).
We store a document in ravendb each time a video is watched:
public class ViewedContent
{
public string Id { get; set; }
public int ProductId { get; set; }
public DateTime DateViewed { get; set; }
}
We're having trouble figuring out how to define the indexes / mapreduces that would best support generating those three reports.
We have tried the following map / reduce.
public class ViewedContentResult
{
public int ProductId { get; set; }
public DateTime DateViewed { get; set; }
public int Count { get; set; }
}
public class ViewedContentIndex :
AbstractIndexCreationTask<ViewedContent, ViewedContentResult>
{
public ViewedContentIndex()
{
Map = docs => from doc in docs
select new
{
doc.ProductId,
DateViewed = doc.DateViewed.Date,
Count = 1
};
Reduce = results => from result in results
group result by result.DateViewed
into agg
select new
{
ProductId = agg.Key,
Count = agg.Sum(x => x.Count)
};
}
}
But, this query throws an error:
var lastSevenDays = session.Query<ViewedContent, ViewedContentIndex>()
.Where( x => x.DateViewed > DateTime.UtcNow.Date.AddDays(-7) );
Error: "DateViewed is not indexed"
Ultimately, we want to query something like:
var lastSevenDays = session.Query<ViewedContent, ViewedContentIndex>()
.Where( x => x.DateViewed > DateTime.UtcNow.Date.AddDays(-7) )
.GroupBy( x => x.ProductId )
.OrderBy( x => x.Count )
This doesn't actually compile, because the OrderBy is wrong; Count is not a valid property here.
Any help here would be appreciated.
Each report is a different GROUP BY if you're in SQL land, that tells you that you need three indexes - one with just the month, one with entries by week, one by month, and one by year (or maybe slightly different depending on how you're actually going to do the query.
Now, you have a DateTime there - that presents some problems - what you actually want to do is index the Year component of the DateTime, the Month component of the date time and Day component of that date time. (Or just one or two of these depending on which report you want to generate.
I'm only para-quoting your code here so obviously it won't compile, but:
public class ViewedContentIndex :
AbstractIndexCreationTask<ViewedContent, ViewedContentResult>
{
public ViewedContentIndex()
{
Map = docs => from doc in docs
select new
{
doc.ProductId,
Day = doc.DateViewed.Day,
Month = doc.DateViewed.Month,
Year = doc.DateViewed.Year
Count = 1
};
Reduce = results => from result in results
group result by new {
doc.ProductId,
doc.DateViewed.Day,
doc.DateViewed.Month,
doc.DateViewed.Year
}
into agg
select new
{
ProductId = agg.Key.ProductId,
Day = agg.Key.Day,
Month = agg.Key.Month,
Year = agg.Key.Year
Count = agg.Sum(x => x.Count)
};
}
}
Hopefully you can see what I'm trying to achieve by this - you want ALL the components in your group by, as they are what make your grouping unique.
I can't remember if RavenDB lets you do this with DateTimes and I haven't got it on this computer so can't verify this, but the theory remains the same.
So, to re-iterate
You want an index for your report by week + product id
You want an index for your report by month + product id
You want an index for your report by year + product id
I hope this helps, sorry I can't give you a compilable example, lack of raven makes it a bit difficult :-)

Categories