LINQ - GroupBy a key and then put each grouped item into separate 'buckets'
You can use GroupBy:
var groups = items.GroupBy(item => item.ListId);
foreach(var group in groups)
{
Console.WriteLine("List with ID == {0}", group.Key);
foreach(var item in group)
Console.WriteLine(" Item: {0}", item.ItemName);
}
LINQ Group By Wrong Results
The Enumerable.GroupBy
method in LINQ to Objects preserves the order of the groupings, as per the docs:
The
IGrouping<TKey,TElement>
objects are yielded in an order based on the order of the elements insource
that produced the first key of eachIGrouping<TKey,TElement>
. Elements in a grouping are yielded in the order they appear insource
.
Which is probably what you expected to see 111...
.
However, you seem to be using GroupBy
on a database table or something like that. This means that you are using Queryable.GroupBy
. According to its docs,
The query behavior that occurs as a result of executing an expression tree that represents calling
GroupBy<TSource,TKey>(IQueryable<TSource>, Expression<Func<TSource,TKey>>)
depends on the implementation of the type of the source parameter. The expected behavior is that it groups the elements of source by a key value that is obtained by invoking keySelector on each element.
As you can see, the exact behaviour is implementation specific, and it doesn't have to preserve the order. Therefore, the first IGrouping
is not necessarily the group with the key true
.
So to get your desired results, either:
- use
Where
to find the group with the keytrue
, or - Load all the objects into memory first, then use LINQ to Object's
GroupBy
. (This could be slow if you have a lot of objects in the database)
Create Buckets ith Linq
Well, for arbitrary list you have to compute range: [min..max]
and then
step = (max - min) / 2;
Code:
// Given
List<double> list = new List<double>() {
0, 0.1, 1.1, 2.2, 3.3, 4.1, 5.6, 6.3, 7.1, 8.9, 9.8, 9.9, 10
};
int n = 5;
// We compute step
double min = list.Min();
double max = list.Max();
double step = (max - min) / 5;
// And, finally, group by:
double[][] result = list
.GroupBy(item => (int)Math.Clamp((item - min) / step, 0, n - 1))
.OrderBy(group => group.Key)
.Select(group => group.ToArray())
.ToArray();
// Let's have a look:
string report = string.Join(Environment.NewLine, result
.Select((array, i) => $"[{min + i * step} .. {min + i * step + step,2}) : {{{string.Join("; ", array)}}}"));
Console.WriteLine(report);
Outcome:
[0 .. 2) : {0; 0.1; 1.1}
[2 .. 4) : {2.2; 3.3}
[4 .. 6) : {4.1; 5.6}
[6 .. 8) : {6.3; 7.1}
[8 .. 10) : {8.9; 9.8; 9.9; 10}
Please, note Math.Clamp
method to ensure [0..n-1]
range for groups keys. If you want a Dictionary<int, double[]>
where Key
is index of bucket:
Dictionary<int, double[]> buckets = list
.GroupBy(item => (int)Math.Clamp((item - min) / step, 0, n - 1))
.ToDictionary(group => group.Key, group => group.ToArray());
Linq group by DateTime.Date and then group by another criterium
Try this, using an anonymous type for the grouping:
group myData by new { myData.TimeStamp.Date,
Hour = (int) myData.TimeStamp.TimeOfDay.TotalMinutes / 60 } into barData
Will C# Take(k) extension method execute a complete previous GroupBy in sequence?
The C# compiler translates your code into a state machine. That is, it creates a new class behind the scenes with state and behavior needed for iterating the student list. Each time you call the code you get an instance of this class.
Will .Take(2) force the complete .GroupByA(x=>x.Group) execution
Looking at the full students.GroupByA(x => x.Group).Take(2)
expression, .Net is able to use the new class instance created by the GroupByA()
with the Take()
function, and you can think of it as execution only continues until the second time your code hits the yield
line, but no further.
However, the nature of a GROUP BY operation is you must loop through the entire dataset to know the attributes of your group, meaning even though you only see the second yield
expression, the source.Where()
call still has to look at your entire data set and make for at least a O(n*m)
operation... every time you identify a new group you go through the entire dataset again.
It should be possible to write a O(n)
GROUP BY operation using a Dictionary rather than a List for finding new groups and accumulating aggregate info in the Dictionary values as you go. You might want to see if you can manage that. Of course, the catch is with small values for n
(small source list sizes) the hash calculations and lookups can cost more than the sequence iterations.
Linq query update all items in groups that meet my condition
Demo on dotnet fiddle
- You need to get all qualified groups by grouping then where the condition
Loop all
StudentsList
list to updateIsQualified
accordingly.var qualifiedGroups = StudentsList
.GroupBy(x => x.GroupID)
.Where(x => x.Any(y => y.University == "OPQ"))
.Select(g => g.Key).ToArray();
StudentsList.ForEach(x => x.IsQualified = Array.IndexOf(qualifiedGroups, x.GroupID) != -1);
foreach(var item in StudentsList)
Console.WriteLine("Group:" + item.GroupID + " Student:" + item.Student + " IsQualified:" + item.IsQualified);
Output
Group:1 Student:John IsQualified:False
Group:1 Student:Jack IsQualified:False
Group:1 Student:Peter IsQualified:False
Group:2 Student:Donald IsQualified:True
Group:2 Student:Olivia IsQualified:True
Group:2 Student:Emity IsQualified:True
Group:2 Student:Emma IsQualified:True
Group:2 Student:Alan IsQualified:True
Group:3 Student:Adam IsQualified:True
Group:3 Student:Jacob IsQualified:True
Group:3 Student:Matthew IsQualified:True
Group:3 Student:Saint IsQualified:True
Group:3 Student:Joshua IsQualified:True
Group:3 Student:Aubrey IsQualified:True
LINQ Group By and merge sublist of Group back into unique list
If you're sure that all the other properties are the same, you can use First
like in your initial attempt and modify @StriplingWarrior's answer like this:
dataInput
.GroupBy(x => x.Id)
.Select(g =>
{
var customer = g.First();
customer.Emails = g.SelectMany(c => c.Emails).ToArray();
return customer;
})
.ToList();
How to group by on result of LINQ group by
In a nutshell,
var result = q.Groupby(x => new { x.a, x.c });
var result = q.Groupby(x => x.a).Select(g => g.Groupby(x => x.c));
Grouping lists into groups of X items per group
Here's one way to do this using LINQ...
public static IEnumerable<IGrouping<int, TSource>> GroupBy<TSource>
(this IEnumerable<TSource> source, int itemsPerGroup)
{
return source.Zip(Enumerable.Range(0, source.Count()),
(s, r) => new { Group = r / itemsPerGroup, Item = s })
.GroupBy(i => i.Group, g => g.Item)
.ToList();
}
Live Demo
how to group data into separate lists with distinct values c#?
Assuming that you want/have a class which maps these properties like:
public class Data
{
public int ID { get; set; }
public int RecordID { get; set; }
public int RecordFieldData { get; set; }
public int RowID { get; set; }
public int FieldID { get; set; }
public string FieldName { get; set; }
}
and you want a List<List<Data>>
which contains a List<Data>
for every unique RowID
+FieldName
combination, you can GroupBy
an anonymous type:
List<List<Data>> allData = data.AsEnumerable()
.GroupBy(r => new { RowID = r.Field<int>("RowID"), FieldName = r.Field<string>("FieldName") })
.Select(g => g
.Select(r => new Data
{
RowID = g.Key.RowID,
FieldName = g.Key.FieldName,
FieldID = r.Field<int>("RowID"),
ID = r.Field<int>("ID"),
RecordFieldData = r.Field<int>("RecordFieldData"),
RecordID = r.Field<int>("RecordID")
})
.ToList())
.ToList();
If you want to use a Dictionary
instead with the RowId
+ FieldName
as key:
Dictionary<Tuple<int, string>, List<Data>> allData = data.AsEnumerable()
.GroupBy(r => new { RowID = r.Field<int>("RowID"), FieldName = r.Field<string>("FieldName") })
.ToDictionary(
g => Tuple.Create(g.Key.RowID, g.Key.FieldName),
g => g.Select(r => new Data
{
RowID = g.Key.RowID,
FieldName = g.Key.FieldName,
FieldID = r.Field<int>("RowID"),
ID = r.Field<int>("ID"),
RecordFieldData = r.Field<int>("RecordFieldData"),
RecordID = r.Field<int>("RecordID")
})
.ToList());
// lookup with RowId + FieldName, f.e.:
List<Data> datas;
if(allData.TryGetValue(Tuple.Create(1, "name"), out datas))
{
// dictionary contains this data
}
Related Topics
How to Create a 2D Array from a CSV File
How to Use Decimal Type in Mongodb
Thread.Sleep() Without Freezing the Ui
C# Find Highest Array Value and Index
Fast Way of Finding Most and Least Significant Bit Set in a 64-Bit Integer
Httpcontext.Current.Request.Files Is Always Empty
Get All Column Names of a Datatable into String Array Using (Linq/Predicate)
Download File With Webclient or Httpclient
How to Parse Json List or Object in C#
Digital Sign With Sha256 With C#
How to Get Total Amount of Ram the Computer Has
Generate a Zip File from Azure Blob Storage Files
.Net Core - How to Properly Kill Started Process
Selenium.Webdriver.Chromedriver Slow to Launch - Why
Best Way to Check If a Data Table Has a Null Value in It
Entity Framework Core With Multiple Foreign Key on Same Column