How to GROUP BY without breaking order? - c#

I have a very simple ordered table with the following data:
=============================
| Timestamp | Count |
=============================
| 12/30/2016 | 322 |
| 12/29/2016 | 322 |
| 12/28/2016 | 322 |
| 12/4/2016 | 541 |
| 12/3/2016 | 541 |
| 12/2/2016 | 541 |
| 12/1/2016 | 322 |
| 11/30/2016 | 322 |
| 11/29/2016 | 322 |
=============================
I would like to SELECT MAX([Timestamp]), Count FROM [dbo].[Table] GROUP BY [Count] without breaking [Timestamp] order, i.e. the output should be:
=============================
| Timestamp | Count |
=============================
| 12/30/2016 | 322 |
| 12/4/2016 | 541 |
| 12/1/2016 | 322 |
=============================
So 12/1/2016 | 322 is not hidden by the GROUP BY.
Does anybody know how to write such query? I need an EntityFramework Linq query and it would be great to know a T-SQL analogue.

I think you need a query like this:
select [Timestamp], [Count]
from (
select *
--//Step 3: Now just find the max value
, row_number() over (partition by seqGroup order by [Timestamp] desc) as seq
from (
select *
--//Step 2: you need to generate a group number for each sequence
, sum(changed) over (order by [Timestamp] desc) as seqGroup
from (
select *
--//Step 1: you need to track changes over [Count]
, case when coalesce(lag([Count]) over (order by [Timestamp] desc), [Count]) = [Count] then 0 else 1 end as changed
from yourTable ) t1
) t2
) t3
where seq = 1;

I see previous answer, and on the his basis written LINQ request.
1) Create you initTable:
var initTable = new List<Tuple<DateTime, int>>();
initTable.Add(Tuple.Create(new DateTime(2016, 12, 30), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 29), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 28), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 4), 541));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 3), 541));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 2), 541));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 1), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 11, 30), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 11, 29), 322));
2) Add row index in initTable and order by Timestamp:
//get row index
var table = initTable
.Where(x => true)
.Select((x, i) => new
{
Timestamp = x.Item1.ToString("MM-dd-yyyy"),
Count = x.Item2,
//row index
Index = i
})
.OrderByDescending(x => x.Timestamp);
3) Query
var query = table
//#1 get prev row count
.Select(x => new
{
x.Timestamp,
x.Count,
x.Index,
//previous row count (-1 for first row, not default null)
Prev = table.LastOrDefault(y => y.Index < x.Index) == null ?
-1 :
table.LastOrDefault(y => y.Index < x.Index).Count
})
//#2 set group flag
.Select(x => new
{
x.Timestamp,
x.Count,
x.Index,
x.Prev,
GroupId = x.Count == x.Prev
})
//#3
.Where(x => x.GroupId == false)
.Select(x => new
{
x.Timestamp,
x.Count
});
You can insert it into LinqPad and verify result.
I hope this will be helpfully.

Related

How to group by portions of a grid by quadratic amounts?

I'm facing the following problem: The have one grid and each grid cell has a position. One example of such grid could be the following one:
_________________________________________
| | | | | |
| (0,0) | (1,0) | (2,0) | (3,0) | (4,0) |
|_______|_______|_______|_______|_______|
| | | | | |
| (0,1) | (1,1) | (2,1) | (3,1) | (4,1) |
|_______|_______|_______|_______|_______|
| | | | | |
| (0,2) | (1,2) | (2,2) | (3,2) | (4,2) |
|_______|_______|_______|_______|_______|
| | | | | |
| (0,3) | (1,3) | (2,3) | (3,3) | (4,3) |
|_______|_______|_______|_______|_______|
| | | | | |
| (0,4) | (1,4) | (2,4) | (3,4) | (4,4) |
|_______|_______|_______|_______|_______|
I need to create a function that, given a number N, creates groups of quadratic NxN amounts of that number at most. For instance, for N = 2, each group would contain at most a 2x2 cells, group 1 would contain [(0, 0) (1, 0) (0, 1) (1, 1)], group 2 [(2, 0) (3, 0) (2, 1) (3, 1)], group 3 would just contain [(4, 0) (4, 1)] and so on.
I'm using C#, so this is conceptually a Group by operation, I decided to use LINQ Group By function, however it requires a lambda expression that must calculate a number that must be the same for each group. Therefore I'm looking at an expression that, for my problem with n = 2, must return the same number for [(0, 0) (1, 0) (0, 1) (1, 1)], another number for [(2, 0) (3, 0) (2, 1) (3, 1)], a different one for [(4, 0) (4, 1)], etc...
Which kind of expression could fulfil that property?
Thank you
In order to use a 'Group By'-operation, you need to define a key to group on.
In this case, if it's about a grid, the only possible key I could think of is the result of a calculation based on the index of the row/column in the grid.
I find it a bit difficult to explain the calculation I chose in plain text so I hope the example below does the talk for me.
Does this answer or help with your question?
Test data / settings
var grid = new List<List<string>>();
grid.Add(new List<string>(new[] { "0,0", "1,0", "2,0", "3,0", "4,0" }));
grid.Add(new List<string>(new[] { "0,1", "1,1", "2,1", "3,1", "4,1" }));
grid.Add(new List<string>(new[] { "0,2", "1,2", "2,2", "3,2", "4,2" }));
grid.Add(new List<string>(new[] { "0,3", "1,3", "2,3", "3,3", "4,3" }));
grid.Add(new List<string>(new[] { "0,4", "1,4", "2,4", "3,4", "4,4" }));
// Quadratic group factor.
int n = 2;
Solution 1 - Lambda expression
var result_1 = grid
// Create quadratic groups by calculating the combined index of the row+column with the quadratic group factor.
.SelectMany(r =>
r.GroupBy(c =>
(int)Math.Floor((double)grid.IndexOf(r) / (double)n)
+ "_" +
(int)Math.Floor((double)r.IndexOf(c) / (double)n)
)
)
// Combine all same keys together in one group.
.GroupBy(g => g.Key)
// Get all results per group.
.Select(gg => gg.SelectMany(g => g).ToList())
// ToList() because it's easier to inspect the value of the result while debugging.
.ToList();
// Short version:
var result_2 = grid
.SelectMany(r =>
r.GroupBy(c =>
(int)Math.Floor((double)grid.IndexOf(r) / (double)n) + "_" + (int)Math.Floor((double)r.IndexOf(c) / (double)n)
)
)
.GroupBy(g => g.Key)
.Select(gg => gg.SelectMany(g => g).ToList())
.ToList();
Solution 2 - Oldschool loop, probably easier/better to understand.
var result_3 = new List<List<string>>();
// Range (amount of both 'rows' and 'columns' since it's a grid).
int range = (int)Math.Ceiling((double)grid.Count / (double)n);
// Loop through 'rows'.
for(var y = 0; y < range; y++)
{
int skipRowsAmount = (y * n);
int takeRowsAmount = n;
// Get all 'rows' to split in groups.
var rows = grid.Skip(skipRowsAmount).Take(takeRowsAmount).ToList();
// Loop through 'columns'.
for (var x = 0; x < range; x++)
{
int skipColumnsAmount = (x * n);
int takeColumnsAmount = n;
// Get all 'columns' from all 'rows' to split in groups.
var quadraticColumns = rows.SelectMany(l => l.Skip(skipColumnsAmount).Take(takeColumnsAmount)).ToList();
// Add the quadratic columns group to the result.
result_3.Add(quadraticColumns);
}
}
Edit - Code to change group key from string to integer
.SelectMany(r =>
r.GroupBy(c =>
(((int)Math.Floor((double)grid.IndexOf(r) / (double)n)) * ((int)Math.Ceiling((double)grid.Count / (double)n)))
+
(int)Math.Floor((double)r.IndexOf(c) / (double)n)
)
)

Linq query using Group by and Joins

I have two tables:
Banner
--------------------------------------------------------|
BId | Name | Link | Image | AltText| Target | BActive|
--------------------------------------------------------
|1 | hello| http| a.jpg |helloimg| | 1 |
--------------------------------------------------------
Tracking
------------------------------------------------------
|TId | BId| ICount| CCount| CreateDate |
------------------------------------------------------
|1 | 1 | 102 | 300 | 2015-11-17 00:00:000 |
|2 | 1 | 182 | 100 | 2015-11-14 00:00:000 |
|3 | 1 | 192 | 200 | 2015-11-12 00:00:000 |
------------------------------------------------------
I want to find the Sum of ICount and CCound for each BId between 2015-11-12 and 2015-11-15.
Here is the code I have tried:
from p in Bannertables
join q in BannerTrackings
on p.BannerId equals q.BannerId
group p by p.BannerId into myTab
select new {
BannerId = myTab.Key,
ImpressionCount = myTab.Sum( x=> x.ImpressionCount),
ClickCount = myTab.Sum(x => x.ClickCount)
};
How do I sort the result according to the date range I specified?
Add the followingwhere to your query:
var d1 = new DateTime(2015, 11, 12);
var d2 = new DateTime(2015, 11, 15);
var query=from p in Bannertables
join q in BannerTrackings
on p.BannerId equals q.BannerId
where q.CreateDate>=d1 && q.CreateDate<=d2
group q by p.BannerId into myTab
select new {
BannerId = myTab.Key,
ImpressionCount = myTab.Sum( x=> x.ImpressionCount),
ClickCount = myTab.Sum(x => x.ClickCount)
};
Update
If you want to get access to the Banner's properties, include it in the group as I show below:
var d1 = new DateTime(2015, 11, 12);
var d2 = new DateTime(2015, 11, 15);
var query=from p in Bannertables
join q in BannerTrackings
on p.BannerId equals q.BannerId
where q.CreateDate>=d1 && q.CreateDate<=d2
group new{Banner=p,Tracking=q} by p.BannerId into myTab
select new {
BannerId = myTab.Key,
Bannername=myTab.Select(e=>e.Banner.Name).FirstOrDefault(),
ImpressionCount = myTab.Sum( x=> x.Tracking.ImpressionCount),
ClickCount = myTab.Sum(x => x.Tracking.ClickCount)
};
But if you only want the banner name this is even better:
var d1 = new DateTime(2015, 11, 12);
var d2 = new DateTime(2015, 11, 15);
var query=from p in Bannertables
join q in BannerTrackings
on p.BannerId equals q.BannerId
where q.CreateDate>=d1 && q.CreateDate<=d2
group q by new {p.BannerId,p.Name} into myTab
select new {
BannerId = myTab.Key.BannerId,
BannerName=myTab.Key.Name,
ImpressionCount = myTab.Sum( x=> x.ImpressionCount),
ClickCount = myTab.Sum(x => x.ClickCount)
};

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();

Combine tables using row values as column LINQ C# SQL

I have a users table:
Id | Name | Age
--------------------
1 | Steve | 21
2 | Jack | 17
3 | Alice | 25
4 | Harry | 14
I also have a table containing additional user info:
UId | Key | Value
----------------------
1 | Height | 70
2 | Height | 65
2 | Eyes | Blue
4 | Height | 51
3 | Hair | Brown
1 | Eyes | Green
The UId column links to the Id column in the users table. As you can see, not all users have the same additional info present. Alice doesn't have a height value, Jack is the only one with an eye color value etc.
Is there a way to combine this data into one table dynamically using C# and LINQ queries so that the result is something like this:
Id | Name | Age | Height | Eyes | Hair
------------------------------------------
1 | Steve | 21 | 70 | Green |
2 | Jack | 17 | 65 | Blue |
3 | Alice | 25 | | | Brown
4 | Harry | 14 | 51 |
If a user does not have a value for the column, it can remain empty/null. Does this require some sort of data pivoting?
For the case, your user info fields are constant:
var result = users.GroupJoin(details,
user => user.Id,
detail => detail.Id,
(user, detail) => new
{
user.Id,
user.Name,
user.Age,
Height = detail.SingleOrDefault(x => x.Key == "Height").Value,
Eyes = detail.SingleOrDefault(x => x.Key == "Eyes").Value,
Hair = detail.SingleOrDefault(x => x.Key == "Hair").Value,
});
You can do it by utilising GroupJoin, example:
var users = new List<Tuple<int, string, int>> {
Tuple.Create(1, "Steve", 21),
Tuple.Create(2, "Jack", 17),
Tuple.Create(3, "Alice", 25),
Tuple.Create(4, "Harry", 14)
};
var userInfos = new List<Tuple<int, string, string>> {
Tuple.Create(1, "Height", "70"),
Tuple.Create(2, "Height", "65"),
Tuple.Create(2, "Eyes", "Blue"),
Tuple.Create(4, "Height", "51"),
Tuple.Create(3, "Hair", "Brown"),
Tuple.Create(1, "Eyes", "Green"),
};
var query = users.GroupJoin(userInfos,
u => u.Item1,
ui => ui.Item1,
(u, infos) => new { User = u, Infos = infos });
var result = query.Select(qi => new
{
Id = qi.User.Item1,
Name = qi.User.Item2,
Age = qi.User.Item3,
Height = qi.Infos.Where(i => i.Item2 == "Height").Select(i => i.Item3).SingleOrDefault(),
Eyes = qi.Infos.Where(i => i.Item2 == "Eyes").Select(i => i.Item3).SingleOrDefault(),
Hair = qi.Infos.Where(i => i.Item2 == "Hair").Select(i => i.Item3).SingleOrDefault()
});
First of all I have grouped the user details data using Feature (I have renamed the Key property with Feature to avoid confusion) & UId then I have used group join to combine both results using into g. Finally retrieved the result using specified feature.
var result = from user in users
join detail in details.GroupBy(x => new { x.UId, x.Feature })
on user.Id equals detail.Key.UId into g
select new
{
Id = user.Id,
Name = user.Name,
Age = user.Age,
Height = g.FirstOrDefault(z => z.Key.Feature == "Height") != null ?
g.First(z => z.Key.Feature == "Height").First().Value : String.Empty,
Eyes = g.FirstOrDefault(z => z.Key.Feature == "Eyes") != null ?
g.First(z => z.Key.Feature == "Eyes").First().Value : String.Empty,
Hair = g.FirstOrDefault(z => z.Key.Feature == "Hair") != null ?
g.First(z => z.Key.Feature == "Hair").First().Value : String.Empty,
};
I am getting following output:-
Here is the complete Working Fiddle.
Try this
var list = (from u in context.users
join ud in context.UserDetails on u.Id equals ud.UId
select new
{
u.Id,
u.Name,
u.Age,
ud.Key,
ud.Value
});
var finallist = list.GroupBy(x => new { x.Id, x.Name,x.Age}).Select(x => new
{
x.Key.Id,
x.Key.Name,
x.Key.Age,
Height = x.Where(y => y.Key == "Height").Select(y => y.Value).FirstOrDefault(),
Eyes = x.Where(y => y.Key == "Eyes").Select(y => y.Value).FirstOrDefault(),
Hair = x.Where(y => y.Key == "Hair").Select(y => y.Value).FirstOrDefault()
}).ToList();
try this query
var objlist=( form a in contex.user
join b in contex.UserDetails on a.id equals a.Uid into gj
from subpet in gj.DefaultIfEmpty()
select new { Id=a.id, Name=a.name, Age=a.age, Height =subpet.Height,Eyes=subpet.Eyes, Hair=subpet.Hair}).ToList();

Combining items in a given datatable column using LINQ

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()) };

Categories

Resources