I have an SQL code like;
Select GroupName, sum(LineAmount) as Total, WeekNumber,
ROW_NUMBER() over (partition by WeekNumber order by sum(LineAmount) desc) as RowNum
from
Invoices
where
month(InvoiceDate)=month(getdate())
group by
GroupName,WeekNumber
I would like to convert this to LINQ, but no luck. I am using LINQ to Object. Any help would be appreciated.
TIA
EDIT : Here is some sample data, and the expected result.
public class Invoice
{
public string GroupName { get; set; }
public int LineAmount { get; set; }
public int WeekNum { get; set; }
}
List<Invoice> theData = new List<Invoice>();
theData.Add(new Invoice { GroupName = "A", LineAmount = 1, WeekNum = 1});
theData.Add(new Invoice { GroupName = "A", LineAmount = 2, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 3, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 2, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 3, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 4, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 4, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 3, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 7, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 6, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 5, WeekNum = 2 });
I have removed "where" from my first query as its not a problem at the moment.
theData
.GroupBy(g => new {g.GroupName, g.WeekNum}, (key, gg) => new {key.GroupName, key.WeekNum, Total = gg.Sum(g => g.LineAmount)})
.GroupBy(g => g.WeekNum, (weekNum, gg) => gg.OrderByDescending(g => g.Total).Select((g,i) => new {g.GroupName, g.Total, g.WeekNum, RowNum = i}))
.SelectMany(g => g)
You have not specified the language you need it in. Here is the code in C#
int index = 0;
var filteredInvoices = (from i in invoices
where i.InvoiceDate.Month == DateTime.Now().Month
group i by new { i.GroupName, i.WeekNumber }
into ig
select new {i.GroupName, Total = ig.Sum(i => i.LineAmount), i.WeekNumber, RowNum = ++index}).OrderByDescending(n => n.Total);
filteredInvoices should have the result that you want. Also I am assuming that the i.InvoiceDate is of type DateTime.
Serg Rogovtsev answer gives me expected result. And the below code is what I have done. Don't know which performs better, but results are same.
(theData.GroupBy(f => new { f.GroupName, f.WeekNum})
.Select(r => new {r.Key.WeekNum, r.Key.GroupName, Total = r.Sum(f => f.LineAmount)}))
.GroupBy(r => new {r.WeekNum}).SelectMany(
g =>
g.OrderByDescending(f => f.Total).Select(
(f, index) => new { f.GroupName, f.Total, f.WeekNum, Ix = index + 1 }))
Related
I have a list of users, each user has string array property called Tags. I am trying to get a unique list of tags and a total count, any idea what I a missing here? I am using LinqPad to write my test query, please see the example code:
void Main()
{
List<User> users = new List<User>(){
new User {Id = 1, Tags = new string[]{"tag1", "tag2"}},
new User {Id = 2, Tags = new string[]{"tag3", "tag7"}},
new User {Id = 3, Tags = new string[]{"tag7", "tag8"}},
new User {Id = 4, Tags = new string[]{"tag1", "tag4"}},
new User {Id = 5 },
};
var uniqueTags = users.Where(m=>m.Tags != null).GroupBy(m=>m.Tags).Select(m=> new{TagName = m.Key, Count = m.Count()});
uniqueTags.Dump();
// RESULT should BE:
// tag1 - Count(2)
// tag2 - Count(1)
// tag3 - Count(1)
// tag4 - Count(1)
// tag7 - Count(2)
// tag8 - Count(1)
}
public class User{
public int Id {get;set;}
public string[] Tags {get;set;}
}
You can flatten to IEnumerable<string> before grouping:
var uniqueTags = users.SelectMany(u => u.Tags ?? new string[0])
.GroupBy(t => t)
.Select(g => new { TagName = g.Key, Count = g.Count() } );
LINQPad C# Expression version:
new[] {
new { Id = 1, Tags = new[] { "tag1", "tag2" } },
new { Id = 2, Tags = new[] { "tag3", "tag7" } },
new { Id = 3, Tags = new[] { "tag7", "tag8" } },
new { Id = 4, Tags = new[] { "tag1", "tag4" } },
new { Id = 5, Tags = (string[])null }
}
.SelectMany(u => u.Tags ?? Enumerable.Empty<string>())
.GroupBy(t => t)
.Select(g => new { TagName = g.Key, Count = g.Count() } )
I have the following classes:
public class People
{
public int ID { get; set; }
public List<Right> Rights { get; set; }
}
public class Right
{
public int ID { get; set; }
public int Val { get; set; }
}
With the following values:
People: ID: 1
Right:
ID: 1
Val: 5
Right:
ID: 2
Val: 4
I would like to retrieve all Val rights (in a single string) for the people.
So for people with ID 1 : getting "5,4".
List<People> list = ...;
string result = string.Join(",", (people.First(p => p.ID == 1)).Rights.Select(r => r.Val));
Example :
List<People> people = new List<People>()
{
new People()
{
ID = 1, Rights = new List<Right>()
{
new Right() { ID = 1, Val = 5 },
new Right() { ID = 2, Val = 10 },
}
},
new People()
{
ID = 2, Rights = new List<Right>()
{
new Right() { ID = 1, Val = 6 },
new Right() { ID = 2, Val = 11 },
}
}
};
string result = string.Join(",", (people.First(p => p.ID == 1)).Rights.Select(r => r.Val));
Console.WriteLine(result);
Output: 5, 10
You can use SelectMany to flatten a structure, so something like:
var vals = people.Where(p => p.ID == 1)
.SelectMany(p => p.Rights.Select(r => Val));
var str = String.Join(",", vals.Select(v => v.ToString());
In my MVC project, I have some tables like this:
Form (FormID, SectionID)
Section (SectionID, SectionName)
SectionQuestion (SectionID, QuestionID)
Question (QuestionID, Content)
A form has multiple sections, and a section has some questions.
I can get all question for the FormID. But I want to get a list of sections (which contain questions) for the model.
It means in the view I want to do something like this:
#Model IEnumerable<MedialForm.Models.Sections>
foreach (var section in Model)
{
//Show questions
}
Can you guys help me? :)
You will not see a list of sections for a form, since the Form can only have 1 section. (i.e. the SectionID is defined in Form, and not FormID in Section). However the following Linq query will return the section and the associated questions for a specified FormID:
void Main()
{
var sections =
new []
{
new Section { SectionID = 1, SectionName = "SectionName1" },
new Section { SectionID = 2, SectionName = "SectionName2" }
};
var forms =
new []
{
new Form { FormID = 1, SectionID = 1 },
new Form { FormID = 2, SectionID = 1 },
new Form { FormID = 3, SectionID = 2 },
new Form { FormID = 4, SectionID = 2 }
};
var questions =
new[]
{
new Question { QuestionID = 1, Content = "Question1" },
new Question { QuestionID = 2, Content = "Question2" }
};
var sectionQuestions =
new[]
{
new SectionQuestion { SectionID = 1, QuestionID = 1 },
new SectionQuestion { SectionID = 2, QuestionID = 1 },
new SectionQuestion { SectionID = 2, QuestionID = 2 }
};
var formId = 4;
var result = forms
.Join(
sections,
f => f.SectionID,
s => s.SectionID,
(f, s) => new { Form = f, Section = s })
.Join(
sectionQuestions,
jfs => jfs.Section.SectionID,
sq => sq.SectionID,
(jfs, sq) => new { Form = jfs.Form, Section = jfs.Section, sq.QuestionID })
.Join(
questions,
jfsq => jfsq.QuestionID,
q => q.QuestionID,
(jfsq, q) => new { Form = jfsq.Form, Section = jfsq.Section, Question = q })
.Where(f => f.Form.FormID == formId)
.GroupBy(f => f.Section.SectionID)
.Select(grp => new { SectionID = grp.Key, Questions = grp.Select(g => g.Question)});
Console.WriteLine($"For Form: {formId} the following sections with their questions were found: {String.Join(", ", result.Select(r => $"SectionID: {r.SectionID}, QuestionIDs: [{String.Join(", ", r.Questions.Select(q => q.QuestionID))}]"))}");
}
public class Form
{
public Int32 FormID { get; set; }
public Int32 SectionID { get; set; }
}
public class Section
{
public Int32 SectionID { get; set; }
public String SectionName { get; set; }
}
public class SectionQuestion
{
public Int32 SectionID { get; set; }
public Int32 QuestionID { get; set; }
}
public class Question
{
public Int32 QuestionID { get; set; }
public String Content { get; set; }
}
This will return the following result:
For Form: 4 the following sections with their questions were found: SectionID: 2, QuestionIDs: [1, 2]
Lets assume I have a list of Persons, in the List I have the following objects:
Person = { ID= 1, State="CA"}
Person = { ID= 2, State="PA"}
Person = { ID= 1, State="NY"}
Person = { ID= 1, State="OH"}
Person = { ID= 3, State="FL"}
Person = { ID= 2, State="KC"}
How do I get a new List Where I can have new Persons by ID only once and if repeated get the State value and put it in the new Person object separated by comma. For example the new List will be
Person = { ID= 1, State="CA,NY,OH"}
Person = { ID= 2, State="PA,KC"}
Person = { ID= 3, State="FL"}
Is there a way to achieve this?
Use Enumerable.GroupBy and String.Join:
var result = persons.GroupBy(p => p.ID)
.Select(g => new Person{
ID = g.Key,
State = string.Join(",", g.Select(p => p.State))
}).ToList();
here is the extension method version
var persons = Person.GroupBy(p => p.ID).Select(p => new Person() { ID = p.Key, State = String.Join(",", p.Select(p2 => p2.State).ToArray()) }).ToList();
Help with the linq query to select the object where each date only has item of one value.
So in the following list:
1) 2011-1-4 would be selected because its items are both 25
2) 2011-1-1 would not be selected because it has items 22 and 25
3) 2011-1-2 and 2011-1-3 would be selected because they only have one item.
public class MyClass
{
public DateTime date { get; set; }
public int item { get; set; }
}
static void Main(string[] args)
{
List<MyClass> classes = new List<MyClass>
{
{ new MyClass() { date = new DateTime(2011, 1, 1), item = 22 } },
{ new MyClass() { date = new DateTime(2011, 1, 1), item = 25 } },
{ new MyClass() { date = new DateTime(2011, 1, 2), item = 23 } },
{ new MyClass() { date = new DateTime(2011, 1, 3), item = 24 } },
{ new MyClass() { date = new DateTime(2011, 1, 4), item = 25 } },
{ new MyClass() { date = new DateTime(2011, 1, 4), item = 25 } },
};
}
What about this? Pretty much got from looking at your guys' answers.
var results = classes.GroupBy(m => m.date);
var q = from n in results where n.All( r => r.item == n.First().item) select n;
This is some unedited ugliness that I've run through Linqpad that seems to meet your requirement.
var query = classes.GroupBy(c => c.date)
.Select(g => g.GroupBy(c => c.item))
.Where(sg => sg.Count() == 1)
.SelectMany(sg => sg)
.SelectMany(g => g)
.Select(c => c.date)
.Distinct();
Returns the dates 1/2, 1/3, and 1/4 as per your requirement.
This seems to work, but I wouldn't say I'm happy with it:
var results = from m in classes
group m by new { m.date } into x
where x.GroupBy(y => y.item).Count() == 1
select x;
var results = classes
.GroupBy(m => m.date)
.Where(g => g.Select(x => x.item).Distinct().Count() == 1)
.Select(g => g.Key).ToList();