0
(listProductx, listProductx2) => listProductx.ProductName == listProductx2.ProductName && listProductx.ProductCode == listProductx2.ProductCode

In the above example I wanted to set ProductName and ProductCode dynamically. These are column names; which we can store in an array or anywhere. I wanted to join it dynamically from an array or list.

Pradu
  • 61
  • 6

2 Answers2

2

Assuming you are targeting LINQ to Objects.

Actually Join method requires 3 expressions (funcs) - outer key selector, inner key selector and result selector. Since you want to join two enumerables of the same type, outer and inner key selectors will be one and the same.

If the count of the properties is no more than 7, we can use Tuple<> as a key. Here is how we can build selector dynamically:

static Func<T, object> CreateSelector<T>(IEnumerable<string> propertyNames)
{
    var sourceType = typeof(T);
    var parameter = Expression.Parameter(sourceType, "e");
    var properties = propertyNames.Select(name => Expression.PropertyOrField(parameter, name)).ToArray();
    var selector = Expression.Lambda<Func<T, object>>(
        Expression.Call(typeof(Tuple), "Create", properties.Select(p => p.Type).ToArray(), properties),
        parameter);
    return selector.Compile();
}

Then we can create a helper method that is using it (put both methods in some top level public static class of your choice):

public static IEnumerable<Tuple<T, T>> Join<T>(this IEnumerable<T> left, IEnumerable<T> right, IEnumerable<string> propertyNames)
{
    var keySelector = CreateSelector<T>(propertyNames);
    return left.Join(right, keySelector, keySelector, Tuple.Create);
}

and now you can use something like this:

class Product
{
    public string ProductName { get; set; }
    public string ProductCode { get; set; }
}

List<Product> list1 = ...;
List<Product> list2 = ...;
var result = list1.Join(list2, new [] { "ProductName", "ProductCode" });
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Can I use above `result` as a parameter for `Func compareValue`. – Pradu Sep 09 '16 at 04:35
  • my function is `public static bool CompareTwoLists(IEnumerable list1, IEnumerable list2, Func compareValue) { return list1.Select(item1 => list2.Any(item2 => compareValue(item1, item2))).All(search => search) && list2.Select(item2 => list1.Any(item1 => compareValue(item1, item2))).All(search => search); }` – Pradu Sep 09 '16 at 04:36
  • I am calling this function `var JoinExp = listProduct.Join(listProduct2, new[] { "ProductName", "ProductCode" });` `bool IsSuccess2 = ListComparison.CompareTwoLists(listProduct, listProduct2, (listProductx, listProductx2) => Convert.ToBoolean(JoinExp))` but am getting error. Unable to cast object of type 'd__37`4 – Pradu Sep 09 '16 at 04:40
  • Thank you... It is working properly now. I have modified calling function `JoinExp.Any()` – Pradu Sep 09 '16 at 05:07
  • Can u change that Join expression two lists; like . because i need to join from two different list with same properties – Pradu Sep 09 '16 at 11:42
  • I already did that here http://stackoverflow.com/questions/39405905/how-to-solve-the-error-cannot-be-inferred-from-the-usage-try-specifying-the-typ/39406987#39406987 - the `Join` method – Ivan Stoev Sep 09 '16 at 11:49
0

If I understand correctly, you want to build the comparison expression dynamically. You can do that based on the documentation from https://msdn.microsoft.com/en-us/library/mt654267.aspx and SO question How do I dynamically create an Expression<Func<MyClass, bool>> predicate from Expression<Func<MyClass, string>>?. The general approach is the same, you just need to build the expression tree differently.

Community
  • 1
  • 1
kiziu
  • 1,111
  • 1
  • 11
  • 15