I have a custom sort that I am using to sort a list which works fine
public static void Sort<T>(ref List<T> list, string propertyName, SortDirection direction)
{
var comparer = new CustomComparer();
list = direction == SortDirection.Ascending
? list.OrderBy(x => x.GetType().GetProperty(propertyName).GetValue(x, null)).ToList()
: list.OrderByDescending(x => x.GetType().GetProperty(propertyName).GetValue(x, null)).ToList();
}
now I'm trying to add a CustomComparer to the mix and I get an error when I extend the method.
The type arguments for method 'IOrderedEnumerable
System.Linq.Enumerable.OrderBy(this
IEnumerable, Func, IComparer)' cannot be
inferred from the usage. Try specifying the type arguments explicitly.
public static void Sort<T>(ref List<T> list, string propertyName, SortDirection direction)
{
list = direction == SortDirection.Ascending
? list.OrderBy(x => x.GetType().GetProperty(propertyName).GetValue(x, null), new CustomComparer()).ToList()
: list.OrderByDescending(x => x.GetType().GetProperty(propertyName).GetValue(x, null), new CustomComparer()).ToList();
}
I get that the OrderBy is not set correctly does anyone have any suggestions?
Thanks.
public class CustomComparer : IComparer<object>
{
public int Compare(object x, object y)
{
}
}
Specify type arguments explicitly <T, object> in OrderByDescending method.
public class MyComparer : IComparer<object>
{
public int Compare(object x, object y)
{
throw new NotImplementedException();
}
}
public static void Sort<T>(ref List<T> list, string propertyName)
{
list = list.OrderByDescending<T, object>(x => x.GetType().GetProperty(propertyName).GetValue(x, null), new MyComparer()).ToList();
}
Related
How do I create a generic method which can take in a sortExpression based on MyObject properties, something like this :
void CreateSortedReport(IList<MyObject> list, Expression<Func<MyObject, TSort>> sortExpression, bool ascending = true)
{
//sort the [list] by sortExpression and by direction;
}
so that I can use it like this:
CreateSortedReport(myItems, x=>x.Name);
or
CreateSortedReport(myItems, x=>x.CreateDate);
Edit 1:
The reason I ask for generic method since there are some methods which are very similar:
CreateReportSortedByName(myItems) {
return myItems.OrderBy(x=>x.Name);
}
CreateReportSortedByDate(myItems) {
return myItems.OrderBy(x=>x.CreateDate);
}
Assuming MyObject is a concrete type, simply add a single generic parameter (<TSort>) to your method signature:
void CreateReport<TSort>(IList<MyObject> list, Expression<Func<MyObject, TSort>> sortExpression, bool ascending = true)
If you want MyObject to be a generic parameter, your signature will look like this:
void CreateReport<MyObject, TSort>(IList<MyObject> list, Expression<Func<MyObject, TSort>> sortExpression, bool ascending = true)
To use the inbuilt sort function to provide an in-place sort, you'd need to write a class that implements IComparer<T>.
For example:
class CompareWithDelegate<TOnObject, TSort> : IComparer<TOnObject>
{
Func<TOnObject, TSort> evaluator;
IComparer comparer = Comparer.Default;
bool ascending;
public CompareWithDelegate(Expression<Func<TOnObject, TSort>> expr, bool ascending = true)
{
evaluator = expr.Compile();
this.ascending = ascending;
}
public int Compare(TOnObject left, TOnObject right)
{
var leftVal = evaluator(left);
var rightVal = evaluator(right);
return (ascending ? 1 : -1) * comparer.Compare(leftVal, rightVal);
}
}
And then:
void CreateSortedReport<TSort>(List<MyObject> list, Expression<Func<MyObject, TSort>> sortExpression, bool ascending = true)
{
list.Sort(new CompareWithDelegate<MyObject, TSort>(sortExpression));
list.Dump();
}
Note that the list must be List<MyObject>, not IList<MyObject>.
Alternatively, if you don't need it to be in-place, you have two options:
Change the signature to Func<MyObject, TSort>:
void CreateSortedReport<TSort>(List<MyObject> list, Func<MyObject, TSort> sortExpression, bool ascending = true)
{
var t =
ascending
? list.OrderBy (sortExpression)
: list.OrderByDescending(sortExpression);
}
Or compile the expression on the fly:
void CreateSortedReport<TSort>(List<MyObject> list, Expression<Func<MyObject, TSort>> sortExpression, bool ascending = true)
{
var method = sortExpression.Compile();
var t =
ascending
? list.OrderBy (method)
: list.OrderByDescending(method);
}
You can build an extension method base on the built-in List(of T).Sort(IComparer(of T)).
public static class SortExtension
{
public static void SortBy<T, TProperty>(this List<T> list, Func<T, TProperty> orderby, bool ascending = true)
{
list.Sort(new InnerComparer<T, TProperty>(orderby, ascending));
}
class InnerComparer<T, TProperty> : IComparer<T>
{
private readonly Func<T, TProperty> _property;
private readonly int _ascending;
public InnerComparer(Func<T, TProperty> property, bool ascending)
{
_property = property;
_ascending = ascending ? 1 : -1;
}
int IComparer<T>.Compare(T x, T y)
{
var p1 = _property(x);
var p2 = _property(y);
return _ascending * Comparer<TProperty>.Default.Compare(p1, p2);
}
}
}
usage:
myObjects.SortBy(o => o.MyProperty);
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?]));));
}
I have the following method that compares 2 list (of the same type) and returns the differences. How do I make this method accept lists of any type?
var differences = list1.Where(x => list2.All(x1 => x1.Name != x.Name))
.Union(list2.Where(x => list1.All(x1 => x1.Name != x.Name)));
To get the difference between two sets (with order independency and multiplicity independency), you can use: HashSet<T>.SymmetricExceptWith(IEnumerable<T>).
public static IEnumerable<T> GetSymmetricDifference<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer = null)
{
HashSet<T> result = new HashSet<T>(list1, comparer);
result.SymmetricExceptWith(list2);
return result;
}
In your case, to use it:
var difference = GetSymmetricDifference(list1, list2, new MyComparer());
With a custom comparer:
public class MyComparer : IEqualityComparer<MyType>
{
public bool Equals(MyType x, MyType y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(MyType obj)
{
return obj.Name == null ? 0 : obj.Name.GetHashCode();
}
}
What about this:
var differences = list1.Except(list2).Union(list2.Except(list1));
If I want the common elements in two list, I can use the intersect function:
var listC = listA.Intersect(listB);
But this compare objects. If the lists have objects of type Persons and I would like to get the persons with the same name for example, how could I do that? Where I set the condition of the name property?
Thanks.
Pass it a custom IEqualityComparer<T>.
First, make a class that implements that interface:
public class PersonNameEqualityComparer:IEqualityComparer<Person>
{
public int GetHashCode (Person obj)
{
return obj.Name.GetHashcode ();
}
public bool Equals (Person x, Person y)
{
return x.Name == y.Name;
}
}
Then, all you need to do is pass an instance of that IEqualityComparer to the intersect method.
var result = listA.Intersect(listB, new PersonNameEqualityComparer());
You could extend this to any object and any property, using generics and lambdas:
public class PropertyEqualityComparer<TObject, TProperty> : IEqualityComparer<TObject>
{
Func<TObject, TProperty> _selector;
IEqualityComparer<TProperty> _internalComparer;
public PropertyEqualityComparer(Func<TObject, TProperty> propertySelector, IEqualityComparer<TProperty> innerEqualityComparer = null)
{
_selector = propertySelector;
_internalComparer = innerEqualityComparer;
}
public int GetHashCode(TObject obj)
{
return _selector(obj).GetHashCode();
}
public bool Equals(TObject x, TObject y)
{
IEqualityComparer<TProperty> comparer = _internalComparer ?? EqualityComparer<TProperty>.Default;
return comparer.Equals(_selector(x), _selector(y));
}
}
You could then just use it like this:
var result = listA.Intersect(listB, new PropertyEqualityComparer<Person, string>(p => p.Name));
or like this:
var result = listA.Intersect(listB, new PropertyEqualityComparer<Person, string>(p => p.Age));
and so on.
My Code looks like this :
Collection<NameValueCollection> optionInfoCollection = ....
List<NameValueCollection> optionInfoList = new List<NameValueCollection>();
optionInfoList = optionInfoCollection.ToList();
if(_isAlphabeticalSoting)
Sort optionInfoList
I tried optionInfoList.Sort() but it is not working.
Using the sort method and lambda expressions, it is really easy.
myList.Sort((a, b) => String.Compare(a.Name, b.Name))
The above example shows how to sort by the Name property of your object type, assuming Name is of type string.
If you just want Sort() to work, then you'll need to implement IComparable or IComparable<T> in the class.
If you don't mind creating a new list, you can use the OrderBy/ToList LINQ extension methods. If you want to sort the existing list with simpler syntax, you can add a few extension methods, enabling:
list.Sort(item => item.Name);
For example:
public static void Sort<TSource, TValue>(
this List<TSource> source,
Func<TSource, TValue> selector)
{
var comparer = Comparer<TValue>.Default;
source.Sort((x, y) => comparer.Compare(selector(x), selector(y)));
}
public static void SortDescending<TSource, TValue>(
this List<TSource> source,
Func<TSource, TValue> selector)
{
var comparer = Comparer<TValue>.Default;
source.Sort((x, y) => comparer.Compare(selector(y), selector(x)));
}
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
}
List<Person> people = new List<Person>();
people.Sort(
delegate(Person x, Person y) {
if (x == null) {
if (y == null) { return 0; }
return -1;
}
if (y == null) { return 0; }
return x.FirstName.CompareTo(y.FirstName);
}
);
You need to set up a comparer that tells Sort() how to arrange the items.
Check out List.Sort Method (IComparer) for an example of how to do this...