Intersection of two shared lists with dynamic properties

I have two general lists with several properties for comparison, but I want the key identifiers to be dynamic with List<string>.

So, let's say we have a class:

class A
{
    string Name { get; set; }
    string Color1 { get; set; }
    string Color2 { get; set; }
    string Length { get; set; }
}

Now the user can select from the user interface which properties of the two lists of these objects must intersect in order to select the correct pair. This is saved in List<string>. For example, if the list line contains "Name" and "Color1", only those objects where "Name" and "Color1" overlap will be returned.

I tried to write a function, but, unfortunately, I'm not sure which collection I should use to create shared lists and how to apply the names of these properties to them? If the name of "identifiers" was always the same, this was not a problem for Linq / Lambda;)

Thanks in advance

+4
source share
1 answer

For this you need to use reflection. It works:

public class A
{
    public string Name { get; set; }
    public string Color1 { get; set; }
    public string Color2 { get; set; }
    public string Length { get; set; }

    public static IEnumerable<A> Intersecting(IEnumerable<A> input, List<string> propertyNames)
    { 
        if(input == null)
            throw new ArgumentNullException("input must not be null ", "input");
        if (!input.Any() || propertyNames.Count <= 1)
            return input;

        var properties = typeof(A).GetProperties();
        var validNames = properties.Select(p => p.Name);
        if (propertyNames.Except(validNames, StringComparer.InvariantCultureIgnoreCase).Any())
            throw new ArgumentException("All properties must be one of these: " + string.Join(",", validNames), "propertyNames");

        var props = from prop in properties
                    join name in validNames.Intersect(propertyNames, StringComparer.InvariantCultureIgnoreCase)
                    on prop.Name equals name
                    select prop;
        var allIntersecting = input
            .Select(a => new { 
                Object = a,
                FirstVal = props.First().GetValue(a, null),
                Rest = props.Skip(1).Select(p => p.GetValue(a, null)),
            })
            .Select(x => new { 
                x.Object, x.FirstVal, x.Rest,
                UniqueValues = new HashSet<object>{ x.FirstVal }
            })
            .Where(x => x.Rest.All(v => !x.UniqueValues.Add(v)))
            .Select(x => x.Object);
        return allIntersecting;
    }
}

Sample data:

var aList = new List<A> { 
    new A { Color1 = "Red", Length = "2", Name = "Red" }, new A { Color1 = "Blue", Length = "2", Name = "Blue" },
    new A { Color1 = "Red", Length = "2", Name = "A3" }, new A { Color1 = "Blue", Length = "2", Name = "A3" },
    new A { Color1 = "Red", Length = "3", Name = "Red" }, new A { Color1 = "Blue", Length = "2", Name = "A6" },
};
var intersecting = A.Intersecting(aList, new List<string> { "Color1", "Name" }).ToList();
+2
source

All Articles