fota666
fota666

Reputation: 153

How to find same classes in a list with one different value

I have a list of Privileges. A Privilege class has four properties: Type, AccessType, Value and Action. I want to throw an exception if there are multiple Privileges where the Type, AccessType and Value are the same, BUT the Actions are different.

So for example a list of p1 and p2 would throw an exception:

Privilege p1 = new Privilege{Type = "a", AccessType = "a", Value = "a", Action = "a"};
Privilege p2 = new Privilege{Type = "a", AccessType = "a", Value = "a", Action = "b"};

I would like to use LINQ, but not sure how.

Upvotes: 3

Views: 92

Answers (4)

Tim Schmelter
Tim Schmelter

Reputation: 460208

So you want to allow duplicate type+access-type+value but only if the action is also the same?

bool throwException = pList
    .GroupBy(x => new { x.Type, x.AccessType, x.Value })
    .Any(g => g.Select(p => p.Action).Distinct().Count() > 1);

First i'm building groups of these three properties. Then i check if any of these groups contains multiple actions. Then you can throw the exception.

A small optimization might be(if the duplicate lists are large) if you replace

Distinct().Count() > 1

with

Distinct().Skip(1).Any()

Upvotes: 3

Chrᴉz remembers Monica
Chrᴉz remembers Monica

Reputation: 1904

I think the most simple solution here ist using 2 Any()s.

bool HasDoubleOtherAction = privilegeList.Any(outer => privilegeList
                               .Any(inner => outer.Type == inner.Type
                                             && outer.AccessType == inner.AccessType
                                             && outer.Value == inner.Value
                                             && outer.Action != inner.Action));

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1501926

It sounds like you want to group by Type, AccessType and Value (which you can do with an anonymous type) and then check the actions. Assuming it's okay to have values where the Action is the same in each case, you could write code like this:

// Normally I'd expect you to have some kind of sequence of privileges rather
// than separate variables.
var privileges = new[] { p1, p2 };
var failures = privileges
    // Group the privileges by 3 properties, extracting actions as the values
    .GroupBy(p => new { p.Type, p.AccessType, p.Value }, p => p.Action)
    // Find any groups which have more than one distinct action
    .Where(g => g.Distinct().Skip(1).Any())
    .ToList();

if (failures.Count > 0)
{
    // There's at least one failure. There may be many. Throw
    // whatever exception you want here. This is just an example.
    var individualMessages =
        failures.Select(g => $"{g.Key}: {string.Join(", ", g.Distinct())}");
    throw new Exception(
        $"Invalid privilege combinations: {string.Join("; ", individualMessages})");
}

Here's a complete example showing a couple of failures:

using System;
using System.Collections.Generic;
using System.Linq;

class Privilege
{
    public string Type { get; }
    public string AccessType { get; }
    public string Value { get; }
    public string Action { get; }

    public Privilege(string type, string accessType, string value, string action)
    {
        Type = type;
        AccessType = accessType;
        Value = value;
        Action = action;
    }
}

class Test
{
    static void Main()
    {
        var privileges = new List<Privilege>
        {
            // Bad: should cause an exception
            new Privilege("a", "a", "a", "a"),
            new Privilege("a", "a", "a", "b"),

            // Another bad one; let's check the
            // failure represents both
            new Privilege("x", "y", "z", "action1"),
            new Privilege("x", "y", "z", "action2"),

            // These have one property difference
            // in each case
            new Privilege("b", "a", "a", "a"),
            new Privilege("a", "b", "a", "a"),
            new Privilege("a", "a", "b", "a"),

            // Duplicate type/access/value, but same action too.
            new Privilege("d", "e", "f", "action"),
            new Privilege("d", "e", "f", "action")
        };
        CheckPrivileges(privileges);
    }

    static void CheckPrivileges(IEnumerable<Privilege> privileges)
    {
        var failures = privileges
            // Group the privileges by 3 properties, extracting actions as the values
            .GroupBy(p => new { p.Type, p.AccessType, p.Value }, p => p.Action)
            // Find any groups which have more than one distinct action
            .Where(g => g.Distinct().Skip(1).Any())
            .ToList();

        if (failures.Count > 0)
        {
            // There's at least one failure. There may be many. Throw
            // whatever exception you want here. This is just an example.
            var individualMessages =
                failures.Select(g => $"{g.Key}: {string.Join(", ", g.Distinct())}");
            throw new Exception(
                $"Invalid privilege combinations: {string.Join("; ", individualMessages)}");
        }
    }
}

This produces an exception like this (I've added the line breaks):

Unhandled Exception: System.Exception: Invalid privilege combinations:
{ Type = a, AccessType = a, Value = a }: a, b;
{ Type = x, AccessType = y, Value = z }: action1, action2

Upvotes: 3

Navoneel Talukdar
Navoneel Talukdar

Reputation: 4598

var duplicates = privilegecollection.GroupBy(x => new {x.Type, x.AccessType, x.Value})
                                    .Where(g => g.Count() > 1)
                                    .Select(g => g.Key);

If duplicates count is greater than 0 then you can throw exception.

Upvotes: 0

Related Questions