Given the following flattened "Mapping" class with Properties "UserId" and "PermissionId".
Lets say I have a Set<Mapping> with 5 items:
UserId | PermissionId |
---|---|
A | X |
A | Y |
B | X |
B | Y |
B | Z |
And I want it to be grouped for the UI like this (lets call it "Row" class with properties List<UserId> "UserIds" and List<PermissionId> "PermissionIds"):
UserIds | PermissionIds |
---|---|
A, B | X, Y |
B | Z |
How would I group the Set<Mapping> to a List<Row>? I got no clue how to do it. I mean, is this more math or can it be done kind of easily by .NET?
I know it can be done very easily for the following outcome with GroupBy but I get no further:
UserIds | PermissionIds |
---|---|
A | X, Y |
B | X, Y, Z |
Thank you in advance!
CodePudding user response:
One possible apporach is to group, create one object per group, and then regroup the new objects before creating your Row
objects.
Seeing as you have used characters for UserId
and PermissionId
in your example, I am regarding those properties as char
s in my example.
The idea is roughly:
- Order by
UserId
(not necessary in your specific example, but otherwise) - Group by
PermissionId
- Create one object per grouping, containing
PermissionId
and the collection of associatedUserId
s - Group the objects by the string created by the
UserId
characters - Create one
Row
object per grouping by- converting the group key (user ID string) to a list of
char
s - extracting all the
PermissionId
s from the group
- converting the group key (user ID string) to a list of
It could be implemented as follows:
List<Row> rows = mappings
.OrderBy(m => m.UserId)
.GroupBy(m => m.PermissionId)
.Select(gr => (
PermissionId: gr.Key,
UserIds: gr.Select(m => m.UserId).ToArray() ))
.GroupBy(permissionForUsers => new string(permissionForUsers.UserIds),
( userIds, permissionsForUsers ) => new Row {
UserIds = userIds.ToList(),
PermissionIds = permissionsForUsers.Select(m => m.PermissionId).ToList() })
.ToList();
Example fiddle here.
Suggestion for improving this implementation:
In the first .Select()
operation, use a collection implementation for the UserIds
object field that is comparable, so that the following .GroupBy()
operation is not dependent on creating a string
(for the group key) and then transforming the key back to a char
collection again when creating the Row
object's UserIds
.
CodePudding user response:
Try grouping the data twice. First by PermissionId, then by the joined string of UserId (eg. 'X,Y'):
var groups = list.GroupBy(x => x.PermissionId);
var groups2 = groups.GroupBy(x => string.Join(',', x.Select(g => g.UserId)));
foreach (var group in groups2)
{
Console.WriteLine(group.Key " - " string.Join(',', group.Select(g => g.Key)));
}
Note: for this to work, the items need to be sorted by UserId and PermissionId.