Combining items in a given datatable column using LINQ - c#

I have a datatable which looks like this:
Id | Title | Month | Year |
ebdef240-abb7-4a82-9229-1ed37496da86 | Maths FT | 1 | 2013 |
57504a66-4882-4794-a8b9-af0ead38dc70 | Maths FT | 2 | 2013 |
57504a66-4882-4794-a8b9-af0ead38dc70 | Maths FT | 2 | 2014 |
57504a66-4882-4794-a8b9-af0ead38dc70 | Maths FT | 2 | 2015 |
ebdef239-abb7-4a82-9229-1ed37496da86 | English PT | 1 | 2013 |
ebdef239-abb7-4a82-9229-1ed37496da86 | English PT | 1 | 2014 |
but I would like it to be arranged like this:
Id | Title | Month | Years |
ebdef240-abb7-4a82-9229-1ed37496da86 | Maths FT | 1 | 2013 |
57504a66-4882-4794-a8b9-af0ead38dc70 | Maths FT | 2 | 2013, 2014, 2015 |
ebdef239-abb7-4a82-9229-1ed37496da86 | English PT | 1 | 2013, 2014 |
It maybe that it would make more sense to represent this as a list. I made an attempt at doing this, but am confused as to a) how I can combine the Years (as above, and b) include non-grouped fields, such as the ID (there are others, this is just a few of the columns for simplicity):
From LINQPad:
var objectTable = new DataTable();
objectTable.Columns.Add("Title",typeof(string));
objectTable.Columns.Add("id",typeof(Guid));
objectTable.Columns.Add("Month",typeof(int));
objectTable.Columns.Add("Year",typeof(string));
objectTable.Rows.Add("Maths FT", "ebdef240-abb7-4a82-9229-1ed37496da86", 1, "2013");
objectTable.Rows.Add("Maths FT", "57504a66-4882-4794-a8b9-af0ead38dc70", 2, "2013");
objectTable.Rows.Add("Maths FT", "57504a66-4882-4794-a8b9-af0ead38dc70", 2, "2014");
objectTable.Rows.Add("Maths FT", "57504a66-4882-4794-a8b9-af0ead38dc70", 2, "2015");
objectTable.Rows.Add("English PT", "ebdef239-abb7-4a82-9229-1ed37496da86", 1, "2013");
objectTable.Rows.Add("English PT", "ebdef239-abb7-4a82-9229-1ed37496da86", 1, "2014");
var DataSort = from row in objectTable.AsEnumerable()
group row by new {title = row.Field<string>("Title"), month = row.Field<int>("Month")} into grp
select new
{
Title = grp.Key.title,
Month = grp.Key.month,
};
DataSort.Dump();
Any examples would greatly appreciated.
Thanks.

Perhaps:
var result = objectTable.AsEnumerable()
.Select(r => new { Row = r, Title = r.Field<string>("Title"), Month = r.Field<int>("Month") })
.GroupBy(x => new { x.Title, x.Month })
.Select( g => new {
id = g.First().Row.Field<Guid>("id"),
g.Key.Title,
g.Key.Month,
Year = g.Select(x => x.Row.Field<string>("Year")).ToList()
});
If you want a string with a comma separated list instead of the List<string> for the year-group use Year = string.Join(",", g.Select(x => x.Row.Field<string>("Year"))).
By the way, why is year a string instead of an int?

This will be the LINQ statement for your output
from o in objectTable
group o by new { o.Id, o.Month, o.Title } into g
select new {Id = g.Key.Id, Title = g.Key.Id, Month = g.Key.Month, Years= String.Join(" ", g.Select(x=>x.Year).ToArray()) };

Related

LINQ expression with mulitple count

I will appreciate help on writing the following SQL query with multiple count into a single LINQ expression
select count(MemoNo) as totalOrder,
count(distinct shopId) as shops,
sum(TotalAmount) as totalAmount
from Sales;
Sample data
| MemoNo | shopId | TotalAmount |
|-------------------------------|
| a10| s2| 200|
| a11| s2| 220|
| a12| s3| 100|
| a13| s3| 20|
| a14| s3| 100|
| a15| s4| 50|
| a16| s4| 20|
| a17| s4| 90|
Sample output should look like this
| totalOrder | shops | totalAmount |
|------------|-------|-------------|
| 8 | 3 | 800|
Try this way
var totalOrder = Sales.Select(p => p.MemoNo).Count();
var shop = Sales.Select(p => p.shopId).Distinct().Count();
var totalAmount = Sales.Sum(p => p.TotalAmount);
Updated
Demo on dotnet fiddle
var tempData = Sales.GroupBy(p => p.shopId).Select(g => new
{
shops = g.Count(),
totalAmount = g.Sum(p => p.TotalAmount),
totalOrder = g.Count()
});
var result = new
{
totalOrder = tempData.Sum(p => p.totalOrder),
shops = tempData.Count(),
totalAmount = tempData.Sum(p => p.totalAmount)
};
// Output: { totalOrder = 8, shops = 3, totalAmount = 800 }

Count and Max Columns Group By in LINQ

I have model
public class Rate
{
public int Nr{ get; set; }
public string Rate{ get; set; }
public int Order{ get; set; }
}
and a RateList = List<Rate> like this
Nr | Rate | Order
123 | A | 2
425 | A+ | 1
454 | B | 4
656 | B+ | 3
465 | A | 2
765 | B | 4
Notice that Order always match the Rate (A+ = 1, A = 2, B+ = 3, B = 4, C+ = 5 ...)
I want to count how many time the Rate occoured and display order by the Order
The result should look like this
Rate | Count | Order
A+ | 1 | 1
A | 2 | 2
B+ | 1 | 3
B | 2 | 4
or without column Order
Rate | Count
A+ | 1
A | 2
B+ | 1
B | 2
In SQL I could do like this if I had above list in table Tab
SELECT Rate, COUNT(Rate), Max(Order) from Tab group by Rate
but in LINQ?
I was trying something like this
var rating= RateList.Distinct().GroupBy(x => x.Rate)
.Select(x => new { Rate = x.Key, RateCount = x.Count() })
.OrderBy(x => x.Order);
but didnt work.
Thank You for help.
Your SQL query is equevalent to:
var rating = rateList.GroupBy(x => x.Rate)
.Select(x => new {
Rate = x.Key,
RateCount = x.Count(e => e != null),
Max = x.Max(g => g.Order)
});

Converting SQL to Linq query

I'm trying to get the output of the following query into a Linq query
SELECT SearchQueries.Query,
Clicks.Name,
COUNT (SearchQueries.Query) AS Hits
FROM SearchQueries
INNER JOIN Clicks ON Clicks.SearchqueryId = SearchQueries.Id
GROUP BY SearchQueries.Query, Clicks.Name
ORDER BY Hits DESC
But I can't seem to figure out how to do this;
this is what I have so far
var result =
_db.Clicks.Select(q => q)
.GroupBy(q => q.Name, g => g.Searchquery.Query)
.ToDictionary(g=> g.Key, g => g);
but how would I continue?
the result is something like this:
+---------------+-------------------+------+
|Query | Name | Hits |
+---------------+-------------------+------+
|tag | dnfsklmfnsd | 53 |
|tag2 | dgsqfsdf | 17 |
+---------------+-------------------+------+
The original tables looks like following
SearchQueries;
+---+-------+
|Id | Query |
+---+-------+
| 1 | tag | x 53
| 2 | tag2 | x 17
+---+-------+
Clicks;
+---+-------------------+---------------+
|Id | Name | SearchqueryId |
+---+-------------------+---------------+
| 1 | dnfsklmfnsd | 1 |
| 2 | dgsqfsdf | 2 |
+---+-------------------+---------------+
Try to use GroupBy and Count: (I changed the order to using SearchQueries as "base table" in the expression, just to make it more easy to compare to the SQL-statement)
var result =
_db.SearchQueries
.GroupBy(sq => new { name = sq.Clicks.Name, query = sq.Query)
.Select(sq => new {
Query = sq.Query,
Name = sq.Clicks.Name,
Hits = sq.Count()
})
.OrderByDescending(sq => sq.Hits);
Well, if you have a navigation property Searchquery on Click, as it looks like, you can do
var result =
_db.Clicks
.GroupBy(m => new {name = m.Name, query = m.Searchquery.Query)
.Select(g => new {
Query = g.Key.query,
Name = g.Key.name,
Hits = g.Count()
});

Select all columns but group by only one in linq

I have been looking for a way to get multiple columns but group by only one in SQL and I found some info. However I can not came up with a way to do it in linq.
I have the following toy example table:
| Id | Message | GroupId | Date |
|-------------------------------|
| 1 | Hello | 1 | 1:00 |
| 2 | Hello | 1 | 1:01 |
| 3 | Hey | 2 | 2:00 |
| 4 | Dude | 3 | 3:00 |
| 5 | Dude | 3 | 3:01 |
And I would like to recover all columns for the rows that have a distinct GroupId as follows (with a 'Date' desc order):
| Id | Message | GroupId | Date |
|-------------------------------|
| 1 | Hello | 1 | 1:00 |
| 3 | Hey | 2 | 2:00 |
| 4 | Dude | 3 | 3:00 |
I do not really care about which row is picked from the grouped ones (first, second...) as long as is the only one given that group Id.
I have came out with the following code so far but it does not do what is supposed to:
List<XXX> messages = <MyRep>.Get(<MyWhere>)
.GroupBy(x => x.GroupId)
.Select(grp => grp.OrderBy(x => x.Date))
.OrderBy(y => y.First().Date)
.SelectMany(y => y).ToList();
This will give you one item per group:
List<dynamic> data = new List<dynamic>
{
new {ID = 1, Message = "Hello", GroupId = 1, Date = DateTime.Now},
new {ID = 2, Message = "Hello", GroupId = 1, Date = DateTime.Now},
new {ID = 3, Message = "Hey", GroupId = 2, Date = DateTime.Now},
new {ID = 4, Message = "Dude", GroupId = 3, Date = DateTime.Now},
new {ID = 5, Message = "Dude", GroupId = 3, Date = DateTime.Now},
};
var result = data.GroupBy(item => item.GroupId)
.Select(grouping => grouping.FirstOrDefault())
.OrderByDescending(item => item.Date)
.ToList();
//Or you can also do like this:
var result = data.GroupBy(item => item.GroupId)
.SelectMany(grouping => grouping.Take(1))
.OrderByDescending(item => item.Date)
.ToList();
If you want to control OrderBy then:
var result = data.GroupBy(item => item.GroupId)
.SelectMany(grouping => grouping.OrderBy(item => item.Date).Take(1))
.OrderByDescending(item => item.Date)
.ToList();

How to add List<T> containing item of type List<string> ,int,string into another List

I have a list with complex data
public class CAR
{
public int ID {get ; set ; }
public string Name { get ; set ; }
public string EngineType { get ; set ; }
public List<string> Months { get; set; }
}
Note that Months data is List<string> its max count is 150
List<CAR> A = new List<CAR>();
List<CAR> B = new List<CAR>();
A has follwoing data
ID | Name | EngineType | Months[0] | Months[1] | Months[2] | Months[3] .. | Months[149] |
1 | Zen | 1001 | 1 | 1 | 4 | 5 .. | 6 |
2 | Benz | 2002 | 6 | 4 | 5 | 6 .. | 2 |
3 | Zen | 1001 | 3 | 1 | 7 | 5 .. | 0 |
4 | Zen | 1001 | 2 | 2 | 4 | 5 .. | 6 |
5 | Zen | 2002 | 2 | 2 | 4 | 5 .. | 6 |
6 | Benz | 2002 | 1 | 1 | 1 | 1 .. | 1 |
IF EngineType and Name are same we add those rows and store the result in a single row
Eg : adding rows
row 1 in B = 1 + 3 + 4
row 2 in B = 2 + 6
row 3 in B = 5
B should contain the following op
ID | Name | EngineType | Months[0] | Months[1] | Months[2] | Months[3] ... | Months[149] |
- | Zen | Petrol | 6 | 4 | 15 | 15 .. | 12 |
- | Benz | Diesel | 7 | 5 | 6 | 7 | 3 |
- | Zen | Diesel | 2 | 2 | 4 | 5 .. | 6 |
had months data been separate entity of type integer something else i could have done this
B = from val in A
group val by new val.EngineType into g
select new CAR{
EngineType = g.Key,
Name = g.Name,
Month0 = g.Sum(p => p.Month0),
Month1 = g.Sum(p => p.Month1),
Month2 = g.Sum(p => p.Month2),
.
.
.
.
.
.
Month148 = g.Sum(p => p.Month148),
Month149 = g.Sum(p => p.Month149)
}.ToList<CAR>();
But since its of type List<string> is there a way to get this done?
Thanks a lot!
Use the power of LINQ:
var B = A.GroupBy(x => new { x.Name, x.EngineType })
.Select(g => new Car
{
Name = g.Key.Name,
EngineType = g.Key.EngineType,
Months = g.SelectMany(x => x.Months.Select((y,i) => new { i, y = int.Parse(y) }))
.GroupBy(x => x.i)
.OrderBy(g2 => g2.Key)
.Select(g2 => g2.Sum(x => x.y).ToString()).ToList()
}).ToList();
foreach (CAR c in A)
{
bool blnadded = false;
if (B.Count == 0)
{
B.Add(c);
blnadded = true;
}
else
foreach (CAR d in B)
{
if (d.Name == c.Name && d.EngineType == c.EngineType)
{
for (int i = 0; i < d.Months.Count; i++)
d.Months[i] = (Convert.ToInt32(d.Months[i]) + Convert.ToInt32(c.Months[i])).ToString();
blnadded = true;
}
}
if (blnadded==false)
B.Add(c);
}

Categories

Resources