-1

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.

Rand Random
  • 7,300
  • 10
  • 40
  • 88
  • first of all Do you understand how `Enumerable.GroupBy` works? there is no code which shows how did you tried to use... second your json is no valid – Selvin Oct 26 '22 at 08:46
  • Does this answer your question? [Group by in LINQ](https://stackoverflow.com/questions/7325278/group-by-in-linq) – Selvin Oct 26 '22 at 08:47

2 Answers2

1

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

Eldar
  • 9,781
  • 2
  • 10
  • 35
0

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()
    }));
radoslawik
  • 915
  • 1
  • 6
  • 14