Using LINQ to objects Intersect and Except on a specific property - c#

When I have 2 List<string> objects, then I can use Intersect and Except on them directly to get an output IEnumerable<string>. That's simple enough, but what if I want the intersection/disjuction on something more complex?
Example, trying to get a collection of ClassA objects which is the result of the intersect on ClassA object's AStr1 and ClassB object's BStr; :
public class ClassA {
public string AStr1 { get; set; }
public string AStr2 { get; set; }
public int AInt { get; set; }
}
public class ClassB {
public string BStr { get; set; }
public int BInt { get; set; }
}
public class Whatever {
public void xyz(List<ClassA> aObj, List<ClassB> bObj) {
// *** this line is horribly incorrect ***
IEnumberable<ClassA> result =
aObj.Intersect(bObj).Where(a, b => a.AStr1 == b.BStr);
}
}
How can I fix the noted line to achieve this intersection.

MoreLINQ has ExceptBy. It doesn't have IntersectBy yet, but you could easily write your own implementation, and possibly even contribute it to MoreLINQ afterwards :)
It would probably look something like this (omitting error checking):
public static IEnumerable<TSource> IntersectBy<TSource, TKey>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> keyComparer)
{
HashSet<TKey> keys = new HashSet<TKey>(first.Select(keySelector),
keyComparer);
foreach (var element in second)
{
TKey key = keySelector(element);
// Remove the key so we only yield once
if (keys.Remove(key))
{
yield return element;
}
}
}
If you wanted to perform an intersection on two completely different types which happened to have a common property type, you could make a more general method with three type parameters (one for first, one for second, and one for the common key type).

x ∈ A ∩ B if and only if x ∈ A and x ∈ B.
So, for each a in aObj, you can check if a.AStr1 is in the set of BStr values.
public void xyz(List<ClassA> aObj, List<ClassB> bObj)
{
HashSet<string> bstr = new HashSet<string>(bObj.Select(b => b.BStr));
IEnumerable<ClassA> result = aObj.Where(a => bstr.Contains(a.AStr1));
}

this code:
public IEnumerable<ClassA> xyz(List<ClassA> aObj, List<ClassB> bObj)
{
IEnumerable<string> bStrs = bObj.Select(b => b.BStr).Distinct();
return aObj.Join(bStrs, a => a.AStr1, b => b, (a, b) => a);
}
has passed the following test:
[TestMethod]
public void PropertyIntersectionBasedJoin()
{
List<ClassA> aObj = new List<ClassA>()
{
new ClassA() { AStr1 = "a" },
new ClassA() { AStr1 = "b" },
new ClassA() { AStr1 = "c" }
};
List<ClassB> bObj = new List<ClassB>()
{
new ClassB() { BStr = "b" },
new ClassB() { BStr = "b" },
new ClassB() { BStr = "c" },
new ClassB() { BStr = "d" }
};
var result = xyz(aObj, bObj);
Assert.AreEqual(2, result.Count());
Assert.IsFalse(result.Any(a => a.AStr1 == "a"));
Assert.IsTrue(result.Any(a => a.AStr1 == "b"));
Assert.IsTrue(result.Any(a => a.AStr1 == "c"));
}

Related

Is there a way to store a pointer to a property in C#?

For example, if I wanted to do something like this:
class Program
{
static void Main(string[] args)
{
var dictionary = new Dictionary<string, Property<AddressInfo>>
{
{ "f", (thing) => thing.Foo },
{ "o", (thing) => thing.Foo },
{ "o", (thing) => thing.Foo },
{ "b", (thing) => thing.Bar },
{ "a", (thing) => thing.Bar },
{ "r", (thing) => thing.Bar },
};
}
}
class Thing
{
public string Foo { get; set; }
public string Bar { get; set; }
}
Otherwise I have to store both the getter and the setter as a Func and an Action separately.
I did not find any native way to do it but you can create a struct which would do it.
Disclaimer: I did implement it because it was fun to do. I didn't test so it's just togive an idea and I doubt it's efficient.
public class PropertyHolder<T, Y>
{
private static object[] emptyArray = new object[0];
public PropertyHolder(Y instance, string propertyName)
{
var property = typeof(Y).GetProperty(propertyName);
var setMethod = property.SetMethod;
var getMethod = property.GetMethod;
Set = (t) => setMethod.Invoke(instance, new object[]{t});
Get = () => (T) getMethod.Invoke(instance, emptyArray);
}
public Action<T> Set { get; private set; }
public Func<T> Get { get; private set; }
}
You can use it this way:
public class Toto
{
public int TheInt { get; set; }
}
var x = new Toto();
var propPointer = new PropertyHolder<int,Toto>(x, nameof(Toto.TheInt));
Is there a way to store a pointer to a property in C#?
No, basically.
You can get pretty close if it is ref-return property, but you can't store a ref either - you can only use it on the stack.

LINQ order by delegate

I have a collection of objects to be ordered by an object's field value. Current problem is that the order depends on a business logic.
public enum Order : byte {
a = 1,
b = 2,
c = 3
}
public class Foo{
public long A {get;set;}
public long B {get;set;}
public long C {get;set;}
}
public class Worker(){
private Foo[] orderFoos(Foo[] foos, Func<Order, long> sort){
return foos.OrderByDescending(f => sort(f)).ToArray(foos.Length);
}
public void Work(){
Foo[] foos = getFoos();
var orderByA = orderFoos(foos, f => f.A);
var orderByB = orderFoos(foos, f => f.B);
var orderByC = orderFoos(foos, f => f.C);
}
}
Compiler throws an error that Argument 1: cannot convert from 'Foo' to 'Order'. Are there any workarounds or solutions?
It seems what you wanted to achieve is sorting on different fields. You may not need to have Order enum if it's only for that purpose and replace:
private Foo[] orderFoos(Foo[] foos, Func<Order, long> sort){
return foos.OrderByDescending(f => sort(f)).ToArray(foos.Length);
}
into
private Foo[] orderFoos(Foo[] foos, Func<Foo, long> sort){
return foos.OrderByDescending(sort).ToArray(foos.Length);
}
NB: I'm not sure your intention with adding foos.Length in the ToArray method, but supposedly that's out of the scope of the question.
The below code seems to work. It is a small change to the orderFoos method, with some sample code for you to test the results.
using System;
using System.Linq;
public enum Order : byte
{
a = 1,
b = 2,
c = 3
}
public class Foo
{
public long A { get; set; }
public long B { get; set; }
public long C { get; set; }
}
public class Worker
{
private Foo[] orderFoos(Foo[] foos, Func<Foo, long> sort)
{
return foos.OrderByDescending(sort).ToArray();
}
public void Work()
{
Foo[] foos = { new Foo() { A = 1, B = 2, C = 3 }, new Foo() { A = 10, B = 1, C = 2 }, new Foo() { A = -1, B = 1, C = 10 } };
var orderByA = orderFoos(foos, f => f.A);
var orderByB = orderFoos(foos, f => f.B);
var orderByC = orderFoos(foos, f => f.C);
Console.WriteLine(orderByA.First().A); // I expect the second to be first here so 10
Console.WriteLine(orderByB.First().A); // I expect the first to be first here so 1
Console.WriteLine(orderByC.First().A); // I expect the third to be first here so -1
}
}
class Program
{
static void Main(string[] args)
{
var worker = new Worker();
worker.Work();
Console.ReadLine();
}
}
#hsoesanto gave a good solution but it doesn't work the way I expected it would be.
So I've created temporary workaround.
private Func<Foo, long> GetOrderFunction(Order orderType)
{
switch (orderType)
{
case Order.A:
return (f) => f.A;
case Order.B:
return (f) => f.B;
case Order.C:
return (f) => f.C;
}
}
private Foo[] orderFoos(Foo[] foos, Order order)
{
var orderFunction = GetOrderFunction(order);
return foos
.OrderByDescending(f => orderFunction (f))
.ToArray(foos.Length);
}

generate all possible combinations for a dynamic list of attributes with their values

I struggle with generating all possible combinations for a List of Attributes with their possible values. What I would like to implement is a Method like this:
public List<Variant> generateAllPossibleVariants(List<Attribute> attributes)
The Attribute Class looks the following:
public class Attribute {
public String Name { get; set; }
public ICollection<String> PossibleValues { get; protected set; }
}
So imagine you have a list of 2 Attributes (while the count is dynamic) with their possible values:
attributeColor with possible Values of ( "red", "blue" )
attributeSize with possible values of ("XL", "L" )
Now my method should return a List of Variant while the Variant Class looks the following:
public class Variant
{
public IDictionary<Attribute, string> AttributeValues { get; private set; }
}
Now my method should return a List of all combinations like the following:
List<Variant> :
Variant.AttributeValues { attributeColor => "red", attributeSize => "XL" }
Variant.AttributeValues { attributeColor => "red", attributeSize => "L" }
Variant.AttributeValues { attributeColor => "blue", attributeSize => "XL" }
Variant.AttributeValues { attributeColor => "blue", attributeSize => "L" }
This is not an optimized code, but you can get the idea and clean it up yourself:
public void Main()
{
var attr1 = new MyAttribute { Name = "Colors", PossibleValues = new List<string> { "red", "blue" } };
var attr2 = new MyAttribute { Name = "Sizes", PossibleValues = new List<string> { "XL", "L" } };
var attr3 = new MyAttribute { Name = "Shapes", PossibleValues = new List<string> { "qube", "circle" } };
var attrList = new List<MyAttribute> { attr1, attr2, attr3 };
var result = attrList.Skip(1).Aggregate<MyAttribute, List<Variant>>(
new List<Variant>(attrList[0].PossibleValues.Select(s => new Variant { AttributeValues = new Dictionary<MyAttribute, string> { {attrList[0], s} } })),
(acc, atr) =>
{
var aggregateResult = new List<Variant>();
foreach (var createdVariant in acc)
{
foreach (var possibleValue in atr.PossibleValues)
{
var newVariant = new Variant { AttributeValues = new Dictionary<MyAttribute, string>(createdVariant.AttributeValues) };
newVariant.AttributeValues[atr] = possibleValue;
aggregateResult.Add(newVariant);
}
}
return aggregateResult;
});
}
public class MyAttribute
{
public string Name { get; set; }
public ICollection<string> PossibleValues { get; set; }
}
public class Variant
{
public IDictionary<MyAttribute, string> AttributeValues { get; set; }
}
You are looking for cartesian product (with dynamic dimension).
One simple way to achieve it is using recursion on the dimension, and each time invoke cartesian product on the result of the recursion, and one of the dimensions.
Pseudo code:
genAllPossibilities(list attributes) //each element in attributes is a list
if length(attributes) == 1:
return attributes[0] //the only element, which is a list of one attribute
else:
curr = head(attributes) // first element in the list
reminder = tails(attributes) // a list of all elements except the first
return cartesianProduct(genAllPossibilities(reminder), curr)
cartesianProduct(list1, list2):
l = new list
for each x1 in list1:
for each x2 in list2:
l.append(new MyObject(x1,x2))
return l

Dynamic linq order by on nested property with null properties

I'm using this dynamic linq orderby function which I got from here.
This works fine with nested properties so I could do this:
var result = data.OrderBy("SomeProperty.NestedProperty");
The problem is that if SomeProperty is null then performing the OrderBy on the NestedProperty throws the infamous "Object reference not set to an instance of an object".
My guess is that I need to customize the following lines to handle the exception:
expr = Expression.Property(expr, pi);
// Or
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
I thought about creating a statement body where I could in the worst case scenario use a try catch but that didn't work as you can't have statement bodies within orderby linq statements: "A lambda expression with a statement body cannot be converted to an expression tree"
I'm lost over here, any suggestions on how I can accomplish this?
By the way, this is for Linq to Objects, not database related.
static void Main(string[] args)
{
var data = new List<MyType>() {
new MyType() { SomeProperty = new Inner() { NestedProperty = "2" }},
new MyType() { SomeProperty = new Inner() { NestedProperty = "1" }},
new MyType() { SomeProperty = new Inner() { NestedProperty = "3" }},
new MyType(),
}.AsQueryable();
var sorted = data.OrderBy(x => GetPropertyValue(x, "SomeProperty.NestedProperty"));
foreach (var myType in sorted)
{
try
{
Console.WriteLine(myType.SomeProperty.NestedProperty);
}
catch (Exception e)
{
Console.WriteLine("Null");
}
}
}
public static object GetPropertyValue(object obj, string propertyName)
{
try
{
foreach (var prop in propertyName.Split('.').Select(s => obj.GetType().GetProperty(s)))
{
obj = prop.GetValue(obj, null);
}
return obj;
}
catch (NullReferenceException)
{
return null;
}
}
How about generics:
Helper Method:
public static Expression<Func<TEntity, TResult>> GetExpression<TEntity, TResult>(string prop)
{
var param = Expression.Parameter(typeof(TEntity), "p");
var parts = prop.Split('.');
Expression parent = parts.Aggregate<string, Expression>(param, Expression.Property);
Expression conversion = Expression.Convert(parent, typeof (object));
var tryExpression = Expression.TryCatch(Expression.Block(typeof(object), conversion),
Expression.Catch(typeof(object), Expression.Constant(null)));
return Expression.Lambda<Func<TEntity, TResult>>(tryExpression, param);
}
Sample Hierarchy:
public class A
{
public A(B b)
{
B = b;
}
public B B { get; set; }
}
public class B
{
public B(C c)
{
C = c;
}
public C C { get; set; }
}
public class C
{
public C(int id)
{
this.Id = id;
}
public int Id { get; set; }
}
Example:
var list = new List<B>
{
new B(new A(new C(1))),
new B(new A(new C(2))),
new B(new A(new C(3))),
new B(new A(null)),
new B(null)
}.AsQueryable();
var ordered = list.OrderByDescending(GetExpression<B, Object>("AProp.CProp.Id"));
Output:
3
2
1
Null
Null

How to join as a string a property of a class?

"I have a List of objects with a property "CustomizationName".
I want to join by a comma the values of that property, i.e.; something like this:
List<MyClass> myclasslist = new List<MyClass>();
myclasslist.Add(new MyClass { CustomizationName = "foo"; });
myclasslist.Add(new MyClass { CustomizationName = "bar"; });
string foo = myclasslist.Join(",", x => x.CustomizationName);
Console.WriteLine(foo); // outputs 'foo,bar'
string foo = String.Join(",", myClasslist.Select(m => m.CustomizationName).ToArray());
If you want, you can turn this into an extension method:
public static class Extensions
{
public static string ToDelimitedString<T>(this IEnumerable<T> source, Func<T, string> func)
{
return ToDelimitedString(source,",",func);
}
public static string ToDelimitedString<T>(this IEnumerable<T> source, string delimiter, Func<T, string> func)
{
return String.Join(delimiter, source.Select(func).ToArray());
}
}
Usage:
public class MyClass
{
public string StringProp { get; set; }
}
.....
var list = new List<MyClass>();
list.Add(new MyClass { StringProp = "Foo" });
list.Add(new MyClass { StringProp = "Bar" });
list.Add(new MyClass { StringProp = "Baz" });
string joined = list.ToDelimitedString(m => m.StringProp);
Console.WriteLine(joined);

Categories

Resources