Home > OS >  GroupBy after GroupBy
GroupBy after GroupBy

Time:10-26

I have the following DTO:

class Permission
{
    public string ObjectId { get; set; }
    public string ObjectType { get; set; }
    public string Scope { get; set; }
    public string? AccountId { get; set; }
    public string? GroupId { get; set; }
}

var permissions = new List<Permission>()
{
    new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = "1", GroupId = null },
    new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = "2", GroupId = null },
    new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = null, GroupId = "1" },
    new(){ ObjectId = "1", ObjectType = "link", Scope = "link:write", AccountId = "2", GroupId = null },
    new(){ ObjectId = "2", ObjectType = "link", Scope = "link:read", AccountId = "1", GroupId = null },
};

I want the following outcome:

{[
    "1": {
        "read": {
            "accounts": ["1", "2"],
            "groups": ["1", "2", "3"]
        },
        "write": {
            "accounts": ["1", "2"],
            "groups": ["1", "2", "3"]
        }
  }
]}

Basically an array of objects of Permission, grouped by their ObjectId property, and then grouped by each Scope that contains an array of the AccountId or GroupId properties.

Each Permission can have the same ObjectId but different Scope, AccountId and GroupId.

I tried to use GroupBy but that gives me an IGrouping and I'm not sure how to proceed.

CodePudding user response:

You need more than just nested group bys. In your output JSON you have keys as property names. You also need to use ToDictionary to convert values into property keys.

var permissions = new List<Permission>()
        {
            new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = "1", GroupId = null },
            new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = "2", GroupId = null },
            new(){ ObjectId = "1", ObjectType = "link", Scope = "link:read", AccountId = null, GroupId = "1" },
            new(){ ObjectId = "1", ObjectType = "link", Scope = "link:write", AccountId = "2", GroupId = null },
            new(){ ObjectId = "2", ObjectType = "link", Scope = "link:read", AccountId = "1", GroupId = null },
        };
        var result = permissions.GroupBy(r=> r.ObjectId).Select(r=> new {
            r.Key,
            InnerGroups = r.GroupBy(q=> q.Scope.Replace("link:","")).Select(q=> new {
                Scope = q.Key,
                Accounts =  q.Where(z=> z.AccountId != null).Select(z=> z.AccountId).ToArray(),
                Groups = q.Where(z=> z.GroupId != null).Select(z=> z.GroupId).ToArray()
            })
        })
            .ToDictionary(r=> r.Key,r=> r.InnerGroups.ToDictionary(q=> q.Scope,q=> new {q.Accounts,q.Groups}));
        
        var serialized = JsonSerializer.Serialize(result,new JsonSerializerOptions{ WriteIndented=true });

Here is the output :

 {
  "1": {
    "read": {
      "Accounts": [
        "1",
        "2"
      ],
      "Groups": [
        "1"
      ]
    },
    "write": {
      "Accounts": [
        "2"
      ],
      "Groups": []
    }
  },
  "2": {
    "read": {
      "Accounts": [
        "1"
      ],
      "Groups": []
    }
  }
}

Fiddle

CodePudding user response:

You would have to use nested GroupBy to achieve that and then convert the result to the dictionaries:

var result = permissions
    .GroupBy(p => p.ObjectId)
    .ToDictionary(g => g.Key, g => g.GroupBy(g => g.Scope)
    .ToDictionary(g => g.Key, g => new
    {
        accounts = g.Select(per => per.AccountId).Distinct().ToList(),
        groups = g.Select(per => per.GroupId).Distinct().ToList()
    }));
  • Related