How to pass delegates as <T> parameter in function - c#

public delegate bool CompareValue<in T1, in T2>(T1 val1, T2 val2);
public static bool CompareTwoLists<T1, T2>(IEnumerable<T1> list1, IEnumerable<T2> list2, CompareValue<T1, T2> 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);
}
In the above function; how to pass "compareValue" as parameter while calling "CompareTwoLists" function?

With a lambda expression that matches the delegate:
var people = new List<Person>();
var orders = new List<Order>();
bool result = CompareTwoLists(people, orders,
(person, order) => person.Id == order.PersonId);
Or as a reference to a method that matches the delegate:
static bool PersonMatchesOrder(Person person, Order order)
{
return person.Id == order.PersonId;
}
bool result = CompareTwoLists(people, orders, PersonMatchesOrder);

You need to create a method (Normal or Anonymous) that matches that delegate's signature. Below is a sample:
var list1 = new List<string>();
var list2 = new List<int>();
CompareValue<string, int> compareValues = (x, y) => true;
CompareTwoLists(list1, list2, compareValues);
You can also replace the anonymous method, with a normal method:
CompareValue<string, int> compareValues = SomeComparingMethod;
static bool SomeComparingMethod(string str, int number)
{
// code here
}
Another Approach
You can change your method to use Func:
public static bool CompareTwoLists<T1, T2>(IEnumerable<T1> list1, IEnumerable<T2> list2,
Func<T1, T2, bool> compareValue)
{
return list1.All(x => list2.Any(y => compareValue(x, y)))
&& list2.All(x => list1.Any(y => compareValue(y, x)));
}
And Change the caller method to:
Func<User, Role, bool> compareValues =
(u, r) => r.Active
&& u.Something == r.Something
&& u.SomethingElse != r.SomethingElse);

Related

Pass expression to initializer

I would like to pass an expression that represents a variable to used when instantiating an object.
Instead of:
class MyObject : IMyInterface { ... }
var list = db.MyObjects.Where(x => !x.IsDeleted).ToList();
var anotherList = list.Select(x => new AnotherObject() {
Id = x.Id,
Value = x.Value
});
I would like to make this so that a list of objects of IMyInterface can be transformed into another type of list (AnotherObject as example) using defined expressions as so:
var list = db.MyObjects
.Where(x => !x.IsDeleted)
.ToAnotherObjectList(x => x.Id, x => x.Value);
...
public static List<AnotherObject> ToAnotherObjectList<T>(
this IEnumerable<IMyInterface> list,
Expression id,
Expression value)
{
return list.Select(x => new AnotherObject() { Id = id, Value = value }).ToList();
}
I'm not sure how to accomplish this. I know I can use reflection to create objects and set properties by a string but I'm not sure how to pass expressions.
UPDATE
Well, I thought I'd have to do some reflection but it's simpler than what I was thinking. Here's my solution that works in IRL.
public static IEnumerable<AnotherObject> ToAnotherObject<T>(this IEnumerable<T> list, Func<T, int> getId, Func<T, string> getValue, Func<T, bool> getSelected = null) where T : IMyInterface
{
return list.Select(x => new AnotherObject {
Display = getValue(x),
Id = getId(x),
Selected = getSelected != null && getSelected(x),
});
}
You could use a Func<TInput,TReturn> for that. For example:
public static List<AnotherObject> ToAnotherObjectList<T>(
this IEnumerable<T> list,
Func<T, int> getId,
Func<T, object> getValue)
{
return list.Select(x => new AnotherObject() { Id = getId(x), Value = getValue(x) }).ToList();
}
Call:
list.ToAnotherObjectList(i => i.Id, i=> i.Value);
In this example I used Funcs with one parameter (of type T) and return type int/object.

Converting LINQ (OrderBy/ThenBy) Code to CompareTo

I have written a generic sort funciton to sort list and dicitonary. But LINQ doesnt works on Unity due to JIT errors. I want to have the same generics and convert it into myList.Sort() which uses CompraeTo. But Im unable to figure out how to accomplish this as generic as this.
public static List<T> MySort<T>(this List<T> source, Type typeOfObject, bool isAscending = false, params string[] param)
{
if(param.Length == 0)
return source;
if (isAscending)
{
var temp = source.OrderBy (a => (typeOfObject.GetProperty (param [0])).GetValue (a, null));
for (int i=1; i<param.Length; i++)
{
var myVar = i;
temp = temp.ThenBy((a => (typeOfObject.GetProperty(param[myVar])).GetValue (a, null)));
}
return temp.ToList();
}
else
{
var temp = source.OrderByDescending (a => (typeOfObject.GetProperty (param [0])).GetValue (a, null));
for (int i=1; i<param.Length; i++)
{
var myVar = i;
temp.ThenByDescending((a => (typeOfObject.GetProperty(param[myVar])).GetValue (a, null)));
}
return temp.ToList();
}
}
USage of this function
RealEstateItems.MySort(typeof(mIsoObjectExt), true, "UnlockLevel", "Coins", "Diamonds");
My current CompareTo Approac
myList.Sort ((a,b) => {
int result = ((a.Value) as mIsoObjectExt).UnlockLevel.CompareTo(((b.Value) as mIsoObjectExt).UnlockLevel);
// result == 0 ? result = a.Value.Coins.CompareTo(a.Value.Coins);
if(result == 0)
{
result = ((a.Value) as mIsoObjectExt).Coins.CompareTo(((b.Value) as mIsoObjectExt).Coins);
}
else
{
return result;
}
if(result == 0)
{
return ((a.Value) as mIsoObjectExt).Diamonds.CompareTo(((b.Value) as mIsoObjectExt).Diamonds);
}
return result;
});
But Im not satisfied with this i have to do this every time i have to sort even on the same properties. Basically i want to make something like above that i tell the function the type its properties to sort on and it sorts. How can i do this with Compare/CompareTo?
So we're going to need a few different building blocks to begin with. First off, what you're really doing here is sorting each item on a collection of values, as is seen in this other question. We can pull the solution from there to have a comparer for sorting items based on a collection of values:
public class SequenceComparer<T> : IComparer<IEnumerable<T>>
{
private IComparer<T> comparer;
public SequenceComparer(IComparer<T> compareer = null)
{
this.comparer = comparer ?? Comparer<T>.Default;
}
public int Compare(IEnumerable<T> x, IEnumerable<T> y)
{
using (var first = x.GetEnumerator())
using (var second = x.GetEnumerator())
{
while (true)
{
var firstHasMore = first.MoveNext();
var secondHasMore = second.MoveNext();
if (!firstHasMore && !secondHasMore)
return 0;
var lengthComparison = firstHasMore.CompareTo(secondHasMore);
if (lengthComparison != 0)
return lengthComparison;
var nextComparison = comparer.Compare(first.Current, second.Current);
if (nextComparison != 0)
return nextComparison;
}
}
}
}
We also want a way of creating a Comparison<T> delegate (which List.Sort accepts) from a projection delegate. This method is simple enough to write:
public static Comparison<T> CreateComparison<T, TKey>(Func<T, TKey> selector,
IComparer<TKey> comparer = null)
{
comparer = comparer ?? Comparer<TKey>.Default;
return (a, b) => comparer.Compare(selector(a), selector(b));
}
It'll also be useful for us to be able to reverse a Comparison<T> (to handle descending ordering):
public static Comparison<T> Reverse<T>(this Comparison<T> comparison)
{
return (a, b) => comparison(b, a);
}
Now to pull all of the pieces together. We can create a comparison that, for the projection, projects each item into a sequence of values that represent fetching each of the property names from the item using reflection. We can then reverse the comparer if we need a descending sort.
public static void MySort<T>(this List<T> source,
bool isAscending = false,
params string[] properties)
{
var type = typeof(T);
var comparison = CreateComparison((T item) =>
properties.Select(prop => type.GetProperty(prop).GetValue(item)),
new SequenceComparer<object>());
if (!isAscending)
comparison = comparison.Reverse();
source.Sort(comparison);
}
Note that if you can also use the sequence comparer to simplify the LINQ approach:
public static IEnumerable<T> MyOrdering<T>(this IEnumerable<T> source,
bool isAscending = false,
params string[] properties)
{
var type = typeof(T);
Func<T, IEnumerable<object>> selector = item =>
properties.Select(prop => type.GetProperty(prop).GetValue(item))
.ToList();
if (isAscending)
return source.OrderBy(selector, new SequenceComparer<object>());
else
return source.OrderByDescending(selector, new SequenceComparer<object>());
}
You can use Servy's approach with reflection. If you decide against reflection, you can use the below approach, but it still needs the comparison to be provided from the caller.
public class MultiValueComparer<T> : IComparer<T>
{
private IEnumerable<Comparison<T>> _comparisons;
public MultiValueComparer(IEnumerable<Comparison<T>> comparisons)
{
_comparisons = comparisons;
}
public int Compare(T x, T y)
{
foreach (var comparison in _comparisons)
{
var result = comparison(x, y);
if (result != 0)
return result;
}
return 0;
}
}
An extension method which takes a variable number of parameters
public static void Sort<T>(List<T> source, params Comparison<T>[] comparisons)
{
if (comparisons.Count() == 0)
return;
source.Sort(new MultiValueComparer<T>(comparisons));
}
Usage:
Ascending Order:
Sort(samples, (x, y) => x.Name.CompareTo(y.Name), (x, y) => x.Test.CompareTo(y.Test));
Descending Order:
Sort(samples, (x, y) => y.Name.CompareTo(x.Name), (x, y) => y.Test.CompareTo(x.Test));

Passing/Specifty A Property In Generic Method?

I am trying to move some code that I wrote to a more generic method. While the method is longer, the part I am having trouble with is the following :
public static void Test()
{
MyObjectType[] list1 = ListMyObjectTypeMethod1();
MyObjectType[] list2 = ListMyObjectTypeMethod2();
List<MyObjectType> linqAblelist1 = new List<MyObjectType>(list1);
List<MyObjectType> linqAblelist2 = new List<MyObjectType>(list2);
IEnumerable<MyObjectType> toBeAdded = linqAblelist1.Where(x => linqAblelist2.All(y => y.Property1 != x.Property1));
IEnumerable<MyObjectType> toBeDeleted = linqAblelist2.Where(a => linqAblelist1.All(b => b.Property1 != a.Property1));
}
And I am trying to pass in a generic type for MyObjectType, but where I have [How To Set Property Here?] how does one specify that in a parameter for the method?
public static void Test<T>(T[] x, T[] y)
{
List<T> list1 = new List<T>(x);
List<T> list2 = new List<T>(y);
IEnumerable<T> toBeAdded = list1.Where(x => list2.All(y => y.[How To Set Property Here?] != x.[How To Set Property Here?]));
IEnumerable<T> toBeDeleted = list2.Where(a => list1.All(b => b.[How To Set Property Here?])); != a.[How To Set Property Here?]));));
}
Pass in the selection of the property as a Func<T, TProperty>:
public static void Test<T, TProperty>(T[] x, T[] y, Func<T, TProperty> propertySelector)
{
List<T> list1 = new List<T>(x);
List<T> list2 = new List<T>(y);
IEnumerable<T> toBeAdded = list1.Where(x => list2.All(y => !propertySelector(y).Equals(propertySelector(x))));
IEnumerable<T> toBeDeleted = list2.Where(a => !list1.All(b => propertySelector(b).Equals(propertySelector(a))));
}
Then you can call it by specifying a lambda expression for propertySelector:
Test(someArray, someOtherArray, t => t.SomeProperty);
The best option is to introduce a generic type constraint that will make sure T either inherits from a specific class or implements an interface. In either case the class or interface have to declare Property1. E.g. like this:
public static void Test<T>(T[] x, T[] y) where T : IHasProperty1
{
…
}
You need to put some constraints on your generic type.
public static void Test<T>(T[] x, T[] y) where T : <SomeInterface>
{
List<T> list1 = new List<T>(x);
List<T> list2 = new List<T>(y);
IEnumerable<T> toBeAdded = list1.Where(x => list2.All(y => y.PropertyName != x.PropertyName));
IEnumerable<T> toBeDeleted = list2.Where(a => list1.All(b => b.PropertyName)); != a.PropertyName));));
}
You cann add a generic constraint that will ensure that T will have the properties you're expecting. Some thing like:
public static void Test<T>(T[] x, T[] y) where T : MyObjectType
{
List<T> list1 = new List<T>(x);
List<T> list2 = new List<T>(y);
IEnumerable<T> toBeAdded = list1.Where(x => list2.All(y => y.Property1 != x.Property1 ));
IEnumerable<T> toBeDeleted = list2.Where(a => list1.All(b => b.Property1 )); != a.[How To Set Property Here?]));));
}

LINQ: is there a way to supply a predicate with more than one parameter to where clause

wondering if there is a way to do the following:
I basically want to supply a predicate to a where clause with more than one paremeters like the following:
public bool Predicate (string a, object obj)
{
// blah blah
}
public void Test()
{
var obj = "Object";
var items = new string[]{"a", "b", "c"};
var result = items.Where(Predicate); // here I want to somehow supply obj to Predicate as the second argument
}
var result = items.Where(i => Predicate(i, obj));
The operation you want is called "partial evaluation"; it is logically related to "currying" a two-parameter function into two one-parameter functions.
static class Extensions
{
static Func<A, R> PartiallyEvaluateRight<A, B, R>(this Func<A, B, R> f, B b)
{
return a => f(a, b);
}
}
...
Func<int, int, bool> isGreater = (x, y) => x > y;
Func<int, bool> isGreaterThanTwo = isGreater.PartiallyEvaluateRight(2);
And now you can use isGreaterThanTwo in a where clause.
If you wanted to supply the first argument then you could easily write PartiallyEvaluateLeft.
Make sense?
The currying operation (which partially applies to the left) is usually written:
static class Extensions
{
static Func<A, Func<B, R>> Curry<A, B, R>(this Func<A, B, R> f)
{
return a => b => f(a, b);
}
}
And now you can make a factory:
Func<int, int, bool> greaterThan = (x, y) => x > y;
Func<int, Func<int, bool>> factory = greaterThan.Curry();
Func<int, bool> withTwo = factory(2); // makes y => 2 > y
Is that all clear?
Do you expect something like this
public bool Predicate (string a, object obj)
{
// blah blah
}
public void Test()
{
var obj = "Object";
var items = new string[]{"a", "b", "c"};
var result = items.Where(x => Predicate(x, obj)); // here I want to somehow supply obj to Predicate as the second argument
}

Having trouble with LINQ left join on List<FileInfo>

I have two List<FileInfo> lists, SourceFiles and DestFiles. I want to build a LINQ query that will return a list of the items whose filenames are in Source but not in Dest, i.e. a left join.
My data set for SourceFiles is:
folder1\a.txt
folder1\b.txt
folder1\c.txt
folder1\d.txt
DestFiles is:
folder2\a.txt
folder2\b.txt
folder2\c.txt
so the query should return folder1\d.txt.
Following the MSDN example, I've tried using LINQ syntax:
var queryX = from s in SourceFiles
join d in DestFiles
on s.Name equals d.Name
into SourceJoinDest
from joinRow in SourceJoinDest.DefaultIfEmpty()
select new
{
joinRow.FullName
};
and using extension methods:
var query = SourceFiles.GroupJoin(DestFiles,
source => source.Name,
dest => dest.Name,
(source,dest) => new
{
path = source.FullName
}).Select(x => x.path.DefaultIfEmpty())
But neither one of these work; the LINQ syntax version returns Object reference not sent to an instance of an object and the extension version returns Enumeration yielded no results.
I realize that these queries are only returning sets of FullName properties and not the full FileInfo objects; I have code that takes each FullName and returns a FileInfo, and does this for each item in the query to rebuild the list. But if there's a way to return a FileInfo directly from the query, that would be great.
I don't think Join is the ideal tool here. Basically you're looking for an Except. The built in Except doesn't have the overload to specify your properties through lambda. You will have to create your own IEqualityComparer. You could do it, however, like this:
var excepts = SourceFiles.Where(c => !DestFiles.Any(p => p.Name == c.Name)).ToList();
Or, to select just the full path, you can use Select at the end.
var excepts = SourceFiles.Where(c => !DestFiles.Any(p => p.Name == c.Name))
.Select(f => f.FullName).ToList();
I would suggest having extension methods to do quick Except and Intersect.
public static IEnumerable<U> Except<R, S, T, U>(this IEnumerable<R> mainList,
IEnumerable<S> toBeSubtractedList,
Func<R, T> mainListFunction,
Func<S, T> toBeSubtractedListFunction,
Func<R, U> resultSelector)
{
return EnumerateToCheck(mainList, toBeSubtractedList, mainListFunction,
toBeSubtractedListFunction, resultSelector, false);
}
static IEnumerable<U> EnumerateToCheck<R, S, T, U>(IEnumerable<R> mainList,
IEnumerable<S> secondaryList,
Func<R, T> mainListFunction,
Func<S, T> secondaryListFunction,
Func<R, U> resultSelector,
bool ifFound)
{
foreach (var r in mainList)
{
bool found = false;
foreach (var s in secondaryList)
{
if (object.Equals(mainListFunction(r), secondaryListFunction(s)))
{
found = true;
break;
}
}
if (found == ifFound)
yield return resultSelector(r);
}
//or may be just
//return mainList.Where(r => secondaryList.Any(s => object.Equals(mainListFunction(r), secondaryListFunction(s))) == ifFound)
// .Select(r => resultSelector(r));
//but I like the verbose way.. easier to debug..
}
public static IEnumerable<U> Intersect<R, S, T, U>(this IEnumerable<R> mainList,
IEnumerable<S> toIntersectList,
Func<R, T> mainListFunction,
Func<S, T> toIntersectListFunction,
Func<R, U> resultSelector)
{
return EnumerateToCheck(mainList, toIntersectList, mainListFunction,
toIntersectListFunction, resultSelector, true);
}
Now in your case you can do just:
var excepts = SourceFiles.Except(DestFiles, p => p.Name, p => p.Name, p => p.FullName)
.ToList();
Instead of using a join you might be able to handle this with .Except()
var enumerable = sourceFiles.Except(destFiles, new FileInfoComparer<FileInfo>((f1, f2)=>f1.Name == f2.Name, f=>f.Name.GetHashCode()));
.Except() takes an IEqualityComparer<T> which you can write yourself or use a wrapper that takes a lambda.
class FileInfoComparer<T> : IEqualityComparer<T>
{
public FileInfoComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
{
_equals = equals;
_getHashCode = getHashCode;
}
readonly Func<T, T, bool> _equals;
public bool Equals(T x, T y)
{
return _equals(x, y);
}
readonly Func<T, int> _getHashCode;
public int GetHashCode(T obj)
{
return _getHashCode(obj);
}
}
Running it with a few sample data results in the one FileInfo object which contains "d.txt"
You almost did it. But you need to take only those source files, which do not have joined destination files:
var query = from s in SourceFiles
join d in DestFiles
on s.Name equals d.Name into g
where !g.Any() // empty group!
select s;

Categories