linq, group by and count - c#

I have a list of Unions with several members and another table with page hits by Union.
Need a report that list each union, the number of each type of member and the clicks (page views).
clicks are on a separate table with a date and unionID
this is close but doesn't really have the results I want. The count shows the total of members but not the total of clicks. How do i get the total clicks? (count records in clicks table that match the UnionID)
(from c in db.view_Members_Details
join h in db.tbl_Clicks on c.unionID equals h.unionID
group c by new { c.UnionName, h.unionID } into g
select new
{
TotalClicks = g.Count(),
UnionName = g.Key.UnionName,
userTypeID1 = g.Where(x => x.UnionName.Equals(g.Key.UnionName) && x.userTypeID.Equals(1)).Count(),
userTypeID2= g.Where(x => x.UnionName.Equals(g.Key.UnionName) && x.userTypeID.Equals(2)).Count(),
userTypeID3= g.Where(x => x.UnionName.Equals(g.Key.UnionName) && x.userTypeID.Equals(3)).Count(),
}).ToList();
results should be:
Clicks Count | Union Name | userTypeID1 Count | userTypeID2 Count | userTypeID3 Count |

I don't think you need the first condition in your WHERE, because you're already grouping on the UnionName.
g.Count(x => x.userTypeID == 3) //etc for 1, 2, 3
As for the clicks, try the following:
TotalClicks = g.Count(x => x.unionID == g.Key.unionID)

Related

sum distinct values from a column in datagridview

I have a datagridview with two columns like this:
group | quantity
------------------------
chest | 3
legs | 7
back | 2
chest | 1
back | 5
legs | 2
What I'm trying to do is to get the sum of distinct group to a list and use that list for populate another datagridview.
So the result must be in this example:
chest | 4
legs | 9
back | 7
I've tried some linq query code but without any success.
How can I do it?
Here's some Linq queries I tried:
List<string> vv = dataGridView1.Rows.Cast<DataGridViewRow>()
.Where(x => !x.IsNewRow)
// either..
.Where(x => x.Cells[7].Value != null)
//..or or both
.Select(x => x.Cells[7].Value.ToString())
.Distinct()
.ToList();
dataGridView6.DataSource = vv;
EDIT
the group column is being auto filled after a selection of another column combobox, the quantity is filled manually. For the group by I found this code and works but throw an error if a cell is empty:
var Sums = dataGridView1.Rows.Cast<DataGridViewRow>()
.GroupBy(row => row.Cells[7].Value.ToString()) // group column
.Select(g => new { User = g.Key, Sum = g.Sum(row => Convert.ToInt32(row.Cells[1].Value)) });
dataGridView6.DataSource = Sums.ToList();
ok, here the solution that works:
var Sums = dataGridView1.Rows.Cast<DataGridViewRow>()
.Where(row => row.Cells[7].Value != null)
.GroupBy(row => row.Cells[7].Value.ToString()) // group column
.Select(g => new { User = g.Key, Sum = g.Sum(row => Convert.ToInt32(row.Cells[1].Value)) }); // quantity column
dataGridView6.DataSource = Sums.ToList();

Linq join with record of second table

I have looked at many similar question on SO but seems its not that straight forward.
The problem is, most of them are dealing with IEnumerable where in my case I have two IQueryable dbsets.
The situation is somewhat similar to the question here.
Student
id Name
1 a1
2 b1
3 c1
Images
id Image StudentId Status ModifiedOn
1 1.jpg 1 Active 2021-03-12 02:02:32.580
2 2.jpg 1 Deleted 2021-03-12 02:01:32.580
3 3.jpg 2 Deleted 2021-03-12 02:02:32.580
4 4.jpg 2 Deleted 2021-03-12 02:01:32.580
Result should be
id Name Image
1 a1 1.jpg
2 b1 3.jpg
3 c1 NULL
I can do this with TSQL and nested WITH qqueries, where one selects Status = Active, and the other selects Status != Active, then merge these two and select the TOP 1.
But since the requirement is to write the equivalent LINQ, I started with the below query, since I don't know a good way to do a merge of CASE WHEN on Status = Active.
var aquery = context.Images;
var lquery = context.Students;
var result = from l in lquery
join a in aquery on l.Id equals a.StudentId into aGroup
from a in aGroup.OrderByDescending(m => m.ModifiedOn).Take(1)
select new {
l.id,
a.StudentId,
a.Status
};
This failed the dbsets are not IEnumerable. Any idea how to get the correct result?
This query should work:
var query =
from s in context.Students
from i in context.Images
.Where(i => i.StudentId = s.Id)
.OrderBy(i => i.Status == "Active" ? 0 : 1)
.ThenByDescending(i => i.ModifiedOn)
.Take(1)
.DefaultIfEmpty()
select new
{
s.Id,
s.Name,
i.Image
};
IQueryable<Image> images = context.Images.AsQueryable();
IQueryable<Student> students = context.Students;
var result = (from st in students
select new
{
Id = st.Id,
Name = st.Name,
ImageName = images
.OrderBy(x => x.ModifiedAt)
.Where(x => x.Status)
.Where(i=> i.StudentId == st.Id)
.Select(x=> x.ImageName)
.FirstOrDefault()
})
.ToList();
But the easiest option is to define navigation field for images inside Student class:
public class Student{
List<Image> Images {get; private set;}
}
and then:
context.Students
.Select(st=> new
{
Id = st.Id,
Name = st.Name,
ImageName = st.Images
.OrderBy(x => x.ModifiedAt)
.Where(x => x.Status)
.Where(i=> i.StudentId == st.Id)
.Select(x=> x.ImageName)
.FirstOrDefault()
})
.ToList();

Linq - how to write group by query to get quantity of each product

Hello every one I have a data as follows
Item Qty Type
1 2 Purchase
1 3 Sales
1 8 Return
2 5 Purchase
2 4 Sales
2 5 Return
Now I have a requirement of getting quantity of each item by Subtracting Sales and Return with Purchase
And my final output would be
item Qty
1 -9
2 -4
Note : To get quantity: Qty => (Purchase Qty - SalesQty - Return Qty) eg: (2-3-8)
So how can I write this query on LINQ or in SQL
SQL Query:
SELECT Item, SUM(CASE Type WHEN 'Purchase' THEN Qty ELSE -Qty END) AS Qty
FROM Table
GROUP BY Item
LINQ:
Items
.GroupBy(p => p.Item)
.Select(p => new
{
Item = p.Key,
Qty = p.Sum(x => x.Type == "Purchase" ? x.Qty : -x.Qty)
});
If you are using LINQ to SQL, you can do:
var ans = from i in src
group i by i.Item into ig
let PurchaseQty = ig.Where(i => i.Type == "Purchase").Sum(i => i.Qty)
let SalesQty = ig.Where(i => i.Type == "Sales").Sum(i => i.Qty)
let ReturnQty = ig.Where(i => i.Type == "Return").Sum(i => i.Qty)
select new {
Item = ig.Key,
Qty = PurchaseQty - SalesQty - ReturnQty
};
If you are using LINQ to EF 2.2, it may work but will do the grouping client side. If you are using EF 3.x, good luck!

Find duplicates in datatable with multiple columns except two

I am new at coding and trying to check a spreadsheet for duplicate rows. The spreadsheet has 50 columns and every column has to be compared except two. If the rows is duplicated, it will combined them to one row and the amounts in columns REQNUM and AUTHNUM will be summed. Most of the samples I found use "Field("a column name")". Because of the large amount of columns, I want to use a variable that excluded the two I don't need in compare.
Example:
Before. The dots represent more columns
COL1|COL2|COL3|...|REQNUM|AUTHNUM
:-----: | :-----: | :----: |...| :----------: | :-----------: |....
x | y | z |...| 1 | 1
x | y | z |...| 2 | 3
After
COL1|COL2|COL3|...|REQNUM|AUTHNUM
------- | ------ | ------ | ...|------------ | ------------|....
x | y | z |...| 3 | 4
This is the code I have and it seems close but not quite right. I was expecting a result of just duplicate rows, so later I can run it through a foreach that will sum and delete extra rows. dtrow gets me the columns I want.(Thanks to Linq Excluding a column). When I try to use this the variable in my query, I get no results and if I remove the "g.Count() > 1" I get all the rows with them missing the two columns. I would like to keep the all the two columns in the results and not have to add them back in later.
var dtRow = dtExcel.Columns.Cast<DataColumn>().Where(c => c.ColumnName != "REQNUM" && c.ColumnName != "AUTHNUM").ToList();
var checkExcel = dtExcel.Rows.Cast<DataRow>()
.GroupBy(x => dtRow.Select(c => x[c]))
.Where(g => g.Count() > 1)
.Select(gr => gr);
//.CopyToDataTable();
Thank to Ken for help. This worked great for what I needed. I used the groupby clause so I can combine the duplicate into one row and add the number fields. also group by create a key that I use in an IF statement.
var dtRow = dtExcel.Columns.Cast<DataColumn>().Where(c => c.ColumnName != "REQNUM" && c.ColumnName != "AUTHNUM").ToList();
var excelDup = dtExcel.Rows.Cast<DataRow>()
.GroupBy(x => String.Join("", dtRow.Select(c => x[c])))
.Select(g =>
{
var row = g.First();
row.SetField("REQNUM", g.Sum(x => x.Field<double>("REQNUM")));
row.SetField("AUTHNUM", g.Sum(x => x.Field<double>("AUTHNUM")));
return row;
})
.CopyToDataTable();
I also used a where clause to create a variable for datarow compare and no key needed.
//Creates variable with all columns except three. It is used in next query
var dtExcelRow = dtExcel.Columns
.Cast().Where(c => c.ColumnName != "TITLE" && c.ColumnName != "REQSTR" && c.ColumnName != "AUTHSTR").ToList();
var dtListRow = dtList.Columns
.Cast().Where(c => c.ColumnName != "TITLE" && c.ColumnName != "REQSTR" && c.ColumnName != "AUTHSTR").ToList();
// Querys create datarow list for compare
IEnumerable<DataRow> eRow = dtExcel.AsEnumerable()
.Where(w => dtExcelRow.Select(c => w[c]).Any())
.Select(x => x);
IEnumerable<DataRow> lRow = dtList.AsEnumerable()
.Where(w => dtListRow.Select(c => w[c]).Any())
.Select(x => x);
// 1st compare gets list of new records that have changes or are new. 2nd is list of old records being change.
var newRecords = eRow.AsEnumerable().Except(lRow.AsEnumerable(), DataRowComparer.Default);
var oldRecords = lRow.AsEnumerable().Except(eRow.AsEnumerable(), DataRowComparer.Default);
You cannot just group the data by dtRow.Select(c => x[c]) because it is a IEnumerable, they may have the same content but they are still different IEnumerable.
If they are string, you may group the data by the joined string:
x => String.Join("", dtRow.Select(c => x[c]))

Select rows based on group by counts

I have a table (Students) with three columns:
StudentID - MotherID - FatherID
I'm having a hard time understanding how I can form a LINQ query to do the following:
I want to get back a list of all students with less than 'y' number of fullsiblings (same mother id and father id) and less than 'z' number of halfsiblings (same father id different mother id).
Using LINQ, I am able to get the correct rows based on half sibling relation ships, but not full sibling relationships:
var c = studentsDT
.GroupBy(a => new { a.FatherID}).Where(grp => grp.Count() <= halfSiblings)
.SelectMany(grp => grp.Select(r => r))
.GroupBy(a => new { a.MotherID}).Where(grp1 => grp1.Count() <= fullSiblings)
.SelectMany(grp1 => grp1.Select(r1 => r1));
If table data looked like the following:
1 100 200
2 101 200
3 100 200
4 100 200
5 101 200
In the above data snippet, student 1 has two full siblings and two half siblings by father.
Student 2 has one full sibling and three half siblings by father.
If I wanted a list that only had students with no more than two full siblings and no more than 1 half sibling, how could this be achieved?
You're going to want a GroupJoin. Something like this:
from student in Students
join sibling in Students
on student.FatherID equals sibling.FatherID
into siblings
where
siblings.Count(s => s.MotherID == student.MotherID) < fullSiblingLimit &&
siblings.Count(s => s.MotherID != student.MotherID) < halfSiblingLimit
select student
Note that you specified half siblings sharing a father and not a mother.
If your data set is very large, there is room to tweak the query for efficiency.
To get the number of full siblings, you need to specify two keys to group by:
var c = studentsDT
.GroupBy(a => new { a.FatherID, a.MotherID })
.Where(g => g.Count() <= fullSiblings)
.SelectMany(g => g)
.GroupBy(a => a.FatherID)
.Where(g => g.Count() <= halfSiblings)
.SelectMany(g => g);
Note that this counts a full sibling as a half sibling (i.e. it ensures that the total number of full and half siblings is less than halfSiblings).

Categories

Resources