I am selecting the following data from an SQL database table in my controller
var fixtures = from f in _context.Fixtures
where f.MatchDate.Year == year &&
(gender.Contains( f.Gender )) &&
(type.Contains( f.MatchType )) &&
(team.Contains( f.TeamName ))
orderby f.TeamName, f.MatchDate
select f;
return View( "DisplayLeagues", fixtures.ToList() );
and calling the DisplayLeagues view to display it. As you can see from the orderby, it contains more than one team. I want to display a separate table on the webpage for each team. My page declaration contains
Inherits="System.Web.Mvc.ViewPage<IEnumerable<RLSBCWebSite.Domain.Entities.Fixture>>"
as the declaration of the Model. To display the individual lines of the table, I have
<% foreach (var item in Model.AsEnumerable()) { %>
But how do I add a loop control based on the name of the team around the table as a whole? It doesn't seem possible to code
<% foreach (var team in Model.TeamName ....
Something like:
group g by f.TeamName into tmp
select new {TeamName = f.Key, Fixtures = tmp.ToList() }
this will give you one group per team, i.e.
foreach(var grp in query) {
Console.WriteLine(grp.TeamName);
foreach(var fixture in grp.Fixtures) {
// write fixture details
}
}
Note that this changes each item to an IGrouping<string, List<Fixture>>, and note that ToLookup will do something very similar if that is easier.
For the outer loop, which produces a separate table for each team, I used the following
<% foreach (var teamName in Model.AsEnumerable().GroupBy( r => r.TeamName )) {
For the inner loop, which produces the individual matches for each team, I used
<% foreach (var item in Model.AsEnumerable().Where(item => item.TeamName == teamName.Key.ToString())) {
This works very well.
Related
I want to update vmlist by geting values from vlist without using any foreach loop.
For now I am just doing this with foreach loop, but I want to replace this foreach with LINQ
foreach (var item in vlist){
vmlist.Where(list => list.SId==item.SId && list.ParameterId==item.ParameterId && list.WId==item.WId)
.Select(li => { li.Value = item.Value; return li; }).ToList();
}
Your current approach is very inefficient - it's O(N * M) and it creates a list on each iteration.
Using a join would be more efficient - I would still use a foreach loop, but separate the querying part from the update part:
var pairsToUpdate = from original in vmlist
join item in vlist
on new { original.SId, original.ParameterId, original.WId }
equals new { item.SId, item.ParameterId, item.WId }
select new { original, item };
foreach (var pair in pairsToUpdate)
{
pair.original.Value = pair.item.Value;
}
No abuse of Select with side-effects
No extra lists created for no good reason
More efficient selection of items to update
I'm trying to show my items in pagination method.
the first part happened by grouping them by i don't know how can sort them too .
in code Item.page contain each item page number and Item.priority contain
order of item.
now how can i sort by order too ?
this is my code :
"#model IEnumerable<MyProgram.Models.Question>"
foreach (var group in Model.GroupBy((item => item.Page)))
{
<fieldset>
<legend></legend>
#{
foreach (var item in group)
{
if (item.Type == "label")
{
i tired this but nothing came up after item. :
foreach (var group in Model.GroupBy((item => item.Page)).OrderBy(item => item.))
This shall work:
Model.GroupBy(item=>item.page)
.Select(group=>group.OrderBy(I=>I.priority))
You need each group to be ordered not the entire grouped enumerable.
try in second foreach
foreach (var item in group.OrderBy(i => i.priority))
if your only goal is to achieve sorting by two or more parameters use OrderBy(...).ThenBy(...)
I have an ASP.NET MVC application which I have being using with LINQ to SQL for a while now. I have got the hang of replicating most queries in LINQ but there is one that has had me stumped for several days now.
I am trying to select a list of "Progressions" where a condition is met, and I have a list of groups.
My ERD is as follows:
"Group" 1<->Many "Enrolments" Many<->1 "Students" 1<->Many "Progressions"
And the standard SQL would be (Except in the code I have a specific set of groups passed to the function):
SELECT dbo.[Group].GroupID, COUNT(*) AS returning
FROM dbo.[Group] INNER JOIN
dbo.Enrolment ON dbo.[Group].CourseID = dbo.Enrolment.GroupID INNER JOIN
dbo.Student ON dbo.Enrolment.StudentID = dbo.Student.StudentID INNER JOIN
dbo.Progression ON dbo.Student.StudentID = dbo.Progression.StudentID
WHERE (dbo.Progression.IsReturning = 0)
GROUP BY dbo.[Group].GroupID
Now for the Web App. The ASP view "Progression" gets passed the varibale "groups" which is a list of a few selected groups. I am currently using the follwing code, which is very slow (30 secs or more to load page)
<%foreach (var tg in Model)
{%>
<% notreturning = 0; %>
<%foreach (Enrolment e in tg.Enrolments)
{
notreturning = notreturning + e.Student.Progressions.Where(p => !p.IsReturning).Count();
}%>
<tr>
<td><% = notreturning %></td>
</tr>
<%
} %>
I am counting some other stuff too but for this example I'll stick to one. Now obviously this is quite slow because it has to do a foreach for groups, then for each enrolment in the group, so around 10 groups times 20 students in each. I deally I want to do something like the following which eliminates the second foreach:
<%foreach (var tg in Model)
{%>
<% notreturning = 0; %>
<%var test = tg.Enrolments.Where(e => e.Student.Progressions.Where(p => !p.IsReturning)).Count(); %>
<tr>
<td><% = notreturning %></td>
</tr>
<%
} %>
That code doesn't work as the nested where clause doesn't return a bool data type, but I hope it get accross what I'm trying to do here.
I'm not sure if I've explained this very well but if anyone has any ideas I would be very grateful, this has been bugging me for days!
A literal conversion of your SQL is something like:
from g in db.Groups
join e in db.Enrolments on g.CourseID equals e.GroupID
join s in db.Students in e.StudentID equals s.StudentID
join p in db.Progressions on s.StudentID equals p.StudentID
where p.IsReturning == 0
GROUP new {
Group = g,
Enrolment = e,
Student = s,
Progression = p
} by g.GroupID into grouped
select new
{
GroupId = grouped.Key,
Returning = grouped.Count()
};
although g.CourseID equals e.GroupID looks a bit odd!
As an aside, if your end goal is to select a list of Progressions, then I find it easiest to start the query with the Progressions as the first thing being selected rather than with the Groups.
This LINQ query would do what you expressed in the comments:
var groups =
from g in db.Groups
let returningStudents =
from enrolment in g.Enrolments
let student = enrolment.Student
where student.Progressions.Any(p => p.IsReturning)
select student
select new GroupStudentReturnCountDto
{
Name = g.Name,
StudentReturnCount = returningStudents.Count()
};
This query would be very efficient, because it lets the database do the counting and it returns only the data that is actually used. If it still isn't fast enough, just add the right databases indexes and you're done ;-)
I have a class...
class Document
{
public int GroupID { get; set; }
public bool Valid { get; set; }
// more
}
... and a list of instances: IEnumerable<Document> documents. In a first step which runs object by object through this list those documents have been validated, which means: the property Valid will be true for some objects and false for other objects in the list.
Now in a second step I have to do the following:
If for at least one document per document group (defined by all documents with the same GroupID) the flag Valid is false then set Valid to false for all documents of the group.
To do this I have created so far the following code fragment:
var q = from d in documents
group d by d.GroupID;
// q is now of type IEnumerable<IGrouping<int, Document>>
foreach (var dg in q) // dg = "document group", of type IGrouping<int, Document>
{
if (dg.Any(d => !d.Valid))
{
foreach (var d in dg)
d.Valid = false;
}
}
I believe, this does what I want (I didn't test it until now, though) but not very efficiently.
Question: Is there a way to improve this code, especially to move the semantics of the Any method in the outer foreach loop "somehow" into the initial LINQ query, so that q only represents the groups which have at least one invalid document? (Also I am obviously not interested in groups which have only one element, so those groups could be filtered out as well.)
Thank you for suggestions in advance!
I think this does what you want:
var q = from d in documents
group d by d.GroupID into g
where g.Count() > 1 && g.Any(d => !d.Valid)
select g;
foreach (var dg in q)
{
foreach (var d in dg)
{
d.Valid = false;
}
}
The top part in fluent syntax would look like:
var q = documents.GroupBy(d => d.GroupID)
.Where(g => g.Count() > 1 && g.Any(d => !d.Valid));
If you're just trying to set the Valid flag to false if one of the documents is not Valid, you could try grabbing the list of the GroupId's who are not valid and then set all of the documents whose share a group with them to be invalid. Sample code as follows:
//Find the invalid GroupIds.
var invalidIds = documents.Where(d => !d.IsValid).Select(p => p.GroupId).Distinct();
//invalidIds now holds the bad groupIds.
//So we can find out if each document's GroupId is an invalid one, and if it is, mark it as invalid.
documents.Where(d => invalidIds.Contains(d.GroupId)).ToList().ForEach(p => p.IsValid = false);
Hope this helps.
I am always finding myself creating linq expressions that still use nested foreach loops heavily. Below is a simple example of what I'm talking about, and I'd really appreciate it if someone on here can show me how to condense this low-efficiency code into a single linq expression?
The database context (db) has three tables: Blog, Tag, Junc_Tag_Blog. The junction table simply stores a record of blogs with their tags.
Anyway, here's my messy code:
public static Collection<Blog> GetByTag(string tagName)
{
// Get the tag record.
var tag = (from t in db.Tags
where t.Name == tagName
select t).Single();
// Get the list of all junction table records.
var tagJunc = from tj in db.Junc_Tag_Blogs
where tj.Fk_Tag_Id == tag.Id
select tj;
// Get a list of all blogs.
var blogs = from b in db.BlogPosts
select b;
// Work out if each blog is associated with given tag.
foreach(var blog in blogs)
{
foreach(var junc in tagJunc)
{
if(blog.Id == junc.Fk_Blog_Id)
{
// We have a match! - do something with this result.
}
}
}
}
Thanks in advance to the person who can help me clean this code up!
You can construct a query that lets the database find the matches for you.
List<Blog> blogs =
(
from t in tag
where t.Name == tagName
from tj in t.Junc_Tag_Blogs
let b = tj.Blog
select b
).ToList();
var blogsWithGivenTag =
from blog in db.BlogPosts
where blog.BlogTags.Any(bt => bt.Tag.Name == tagName)
select blog;
Read about performing inner joins with LINQ.
You can put the blogs in a dictionary, group the junctions on the blog id, and loop through the groups:
var blogDict = blogs.ToDictionary(b => b.Id);
foreach(var group in tagJunk.GroupBy(j => j.Fk_Blog_Id)) {
if (blogDict.ContainsKey(group.Key)) {
var blog = blogDict[group.Key];
foreach (var junction in group) {
// here you have the blog and the junction
}
}
}
This is also nested loops, but for each blog you only loop through the junctions that actually belong to that blog, instead of all junctions.