LINQ - Sorting a custom list - linq

I want to do the same as explained here:
Sorting a list using Lambda/Linq to objects
that is:
public enum SortDirection { Ascending, Descending }
public void Sort<TKey>(ref List<Employee> list,
Func<Employee, TKey> sorter, SortDirection direction)
{
if (direction == SortDirection.Ascending)
list = list.OrderBy(sorter);
else
list = list.OrderByDescending(sorter);
}
to call it he said to do:
Sort(ref employees, e => e.DOB, SortDirection.Descending);
but I do not understand what TKey is refering to and as I can see in the call it is missed the generic TKey.
Could you explain me what is TKey and how to use it?
I suppose I can use another name for the method, it is not necessary to be Sort, right?
thanks!

You sort by the key which is of type TKey and must implement IComparable<TKey>. For instance:
// key: Firstname
// TKey: string (which is IComparable<String>
list.OrderBy(person => person.Firstname);
The above code sorts by firstname, which is what you define using the sorter. And yes, you can give your method any name you like. It does not have to be named Sort.
Improvement Suggestion (indirectly related to the question)
instead of changing list and passing it as a reference I'd suggest you to consider the following implementation:
public IOrderedEnumerable<Employee> Sort<TKey>(IEnumerable<Employee> list, Func<Employee, TKey> sorter, SortDirection direction);
{
IOrderedEnumerable<Employee> result;
if (direction == SortDirection.Ascending)
result = list.OrderBy(sorter);
else
result = list.OrderByDescending(sorter);
return result;
}
You could then return a new ordered enumerable of Employee objects instead of changing the old one and use any enumerable instead of List object only. This gives you more flexibility and is closer to the LINQ implementation which people tend to be used to.

Related

LINQ Distinct does not invoke IEquatable<T>.Equals

I have a set of domain object, deriving from a base, where I've overridden Equals, IEquatable<T>.Equals and equality operators. I've successfully used Contains, but now I am trying to use Distinct differently. Here's look at a sample code:
var a = new Test { Id = 1 };
var a2 = new Test { Id = 1 };
var list = new List<Test> { a, a2 };
var distinct = list.Distinct().ToList(); // both objects, Equal implementations not called
var containsA = list.Contains(a); // true, Equal implementations called
var containsA2 = list.Contains(a); // true
var containsNewObjectWithSameId = list.Contains(new Test { Id = 1 }); // true
public class Test : IEquatable<Test>
{
public int Id { get; init; }
public bool Equals(Test other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
if (this.GetType() != other.GetType())
return false;
return this.Id == other.Id;
}
public override int GetHashCode() => base.GetHashCode + this.Id;
}
Contains finds matches, but Distinct is feeling very inclusive and keeps them both. From MS docs:
The first search does not specify any equality comparer, which means FindFirst uses
EqualityComparer.Default to determine equality of boxes. That in turn uses the implementation
of the IEquatable.Equals method in the Box class.
What am I missing?
Thanks #JonSkeet for your insight in the comments.
The problem in this case is the way I wrote my GetHashCode method. It has nothing to do with LINQ, as I originally thought.
Explanation
GetHashCode has to be identical for objects that compare equally. In my case - since the base implementation of object.Equals only checks for reference equality and I am comparing two separate objects - a and b, their base.GetHashCode would result in different values, which in turn would render those two objects as not equal.
Solution
In this case, simply returning the Id value is enough as is shown in MS docs:
One of the simplest ways to compute a hash code for a numeric value that has the same or a smaller range than the Int32 type is to simply return that value.
So changing the above code sample like this:
public override int GetHashCode() => this.Id;
would solve the issue. Please keep in mind that if the value of Id is not unique, this will cause ill behavior. In such cases you'll need another property to check and you will have to compose GetHashCode from ALL those properties. For further info refer to MS docs

How do I use a custom comparer with the Linq Distinct method?

I was reading a book about Linq, and saw that the Distinct method has an overload that takes a comparer. This would be a good solution to a problem I have where I want to get the distinct entities from a collection, but want the comparison to be on the entity ID, even if the other properties are different.
According to the book, if I have a Gribulator entity, I should be able to create a comparer like this...
private class GribulatorComparer : IComparer<Gribulator> {
public int Compare(Gribulator g1, Gribulator g2) {
return g1.ID.CompareTo(g2.ID);
}
}
...and then use it like this...
List<Gribulator> distinctGribulators
= myGribulators.Distinct(new GribulatorComparer()).ToList();
However, this gives the following compiler errors...
'System.Collections.Generic.List' does not contain a definition for 'Distinct' and the best extension method overload 'System.Linq.Enumerable.Distinct(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEqualityComparer)' has some invalid arguments
Argument 2: cannot convert from 'LinqPlayground.Program.GribulatorComparer' to 'System.Collections.Generic.IEqualityComparer'
I've searched around a bit, and have seen plenty of examples that use code like this, but no complaints about compiler errors.
What am I doing wrong? Also, is this the best way of doing this? I want a one-off solution here, so don't want to start changing the code for the entity itself. I want the entity to remain as normal, but just in this one place, compare by ID only.
Thanks for any help.
You're implementing your comparer as an IComparer<T>, the LINQ method overload requires an implementation of IEqualityComparer:
private class GribulatorComparer : IEqualityComparer<Gribulator> {
public bool Equals(Gribulator g1, Gribulator g2) {
return g1.ID == g2.ID;
}
}
edit:
For clarification, the IComparer interface can be used for sorting, as that's basically what the Compare() method does.
Like this:
items.OrderBy(x => new ItemComparer());
private class ItemComparer : IComparer<Item>
{
public int Compare(Item x, Item y)
{
return x.Id.CompareTo(y.Id)
}
}
Which will sort your collection using that comparer, however LINQ provides a way to do that for simple fields (like an int Id).
items.OrderBy(x => x.Id);

Use LINQ to select Single from nested collections

I have two classes - MyBaseClass and BaseClassContainer - that are declared like such:
public class MyBaseClass
{
private Guid id;
public Guid ID
{
if (id == Guid.Empty)
{
id = Guid.NewGuid();
}
return id;
}
//...Other Properties omitted for brevity
}
and
public class BaseClassContainer : INotifyPropertyChanged
{
private ObservableCollection<MyBaseClass> baseClasses;
public ObservableCollection<MyBaseClass> BaseClasses
{
//...Omitted for brevity...
}
}
Then in my code I have an ObservableCollection of type BaseClassContainer (BaseClassContainerCollection). What I'm trying to figure out is how can I use LINQ to select a single BaseClassContainer from the ObservableCollection where one of its MyBaseClass.ID matches a specific Guid. The reason I'm using the Single() method is because I know they're all going to be unique.
I've tried the following but it doesn't work:
var result = BaseClassContainerCollection.Single(container => container.BaseClasses.Single(baseClass => baseClass.ID == specificGuid));
I get an error saying: Cannot implicitly convert type 'MyBaseClass' to 'bool'. What am I missing?
Lets break apart your query:
BaseClassContainerCollection.Single(yourPredicate);
Single, as it is used here, basically says "filter BaseClassContainerCollection on this predicate" (a "filter" function that evaluates to true or false for whether or not to include it in the results). Instead of a function that returns true/false, you're saying you want it to evaluate to a MyBaseClass, which doesn't make sense. Your inner call to Single makes sense, because x => x.Id == guid is a function that returns true/false and filters to only those elements that meet the criteria (then states that you know there will only be one of them in the results or else throw an exception).
What you want to do is Select the single MyBaseClass result from the inner query, then call Single on the result (without a predicate) since you know the result should only have one item returned. I believe you're looking for:
BaseClassContainerCollection.Select(container => container.BaseClasses.Single(baseClass => baseClass.ID == specificGuid)).Single();

How to select objects from a list that has a property that matches an item in another list?

Hard question to understand perhaps, but let me explain. I have a List of Channel-objects, that all have a ChannelId property (int). I also have a different List (int) - SelectedChannelIds, that contains a subset of the ChannelId-s.
I want to select (through LINQ?) all the Channel-objects that has a ChannelId-property matching one in the second List.
in other words, I have the following structure:
public class Lists
{
public List<Channel> AllChannels = ChannelController.GetAllChannels();
public List<int> SelectedChannelIds = ChannelController.GetSelectedChannels();
public List<Channel> SelectedChannels; // = ?????
}
public class Channel
{
// ...
public int ChannelId { get; set; }
// ...
}
Any ideas on what that LINQ query would look like? Or is there a more effective way? I'm coding for the Windows Phone 7, fyi.
You can use List.Contains in a Where clause:
public Lists()
{
SelectedChannels = AllChannels
.Where(channel => SelectedChannelIds.Contains(channel.ChannelId))
.ToList();
}
Note that it would be more efficient if you used a HashSet<int> instead of a List<int> for the SelectedChannelIds. Changing to a HashSet will improve the performance from O(n2) to O(n), though if your list is always quite small this may not be a significant issue.
SelectedChannels = new List<Channel>(AllChannels.Where(c => SelectedChannelIds.Contains(c.ChannelId)));

Entity Framework 4.1 simple dynamic expression for object.property = value

I know there is a way to use Expressions and Lambdas to accomplish this but I having a hard time piecing it all together. All I need is a method that will dynamically query an Entity Framework DBSet object to find the row where the propery with the given name matches the value.
My context:
public class MyContext : DbContext
{
public IDbSet<Account> Accoounts{ get { return Set<Account>(); } }
}
The method that I'm looking to write:
public T Get<T>(string property, object value) : where T is Account
{...}
I would rather not have to use Dynamic SQL to accomplish this so no need to suggest it because I already know it's possible. What I'm really looking for is some help to accomplish this using Expressions and Lambdas
Thanks in advance, I know it's brief but it should be pretty self-explanatory. Comment if more info is needed
I'm trying to avoid dynamic linq as much as possible because the main point of linq is strongly typed access. Using dynamic linq is a solution but it is exactly the oppose of the linq purpose and it is quite close to using ESQL and building the query from sting concatenation. Anyway dynamic linq is sometimes real time saver (especially when it comes to complex dynamic ordering) and I successfully use it in a large project with Linq-to-Sql.
What I usually do is defining some SearchCriteria class like:
public class SearchCriteria
{
public string Property1 { get; set; }
public int? Property2 { get; set; }
}
And helper query extension method like:
public static IQueryable<SomeClass> Filter(this IQueryable<SomeClass> query, SearchCriteria filter)
{
if (filter.Property1 != null) query = query.Where(s => s.Property1 == filter.Property1);
if (filter.Property2 != null) query = query.Where(s => s.Property2 == filter.Property2);
return query;
}
It is not generic solution. Again generic solution is for some strongly typed processing of classes sharing some behavior.
The more complex solution would be using predicate builder and build expression tree yourselves but again building expression tree is only more complex way to build ESQL query by concatenating strings.
Here's my implementation:
public T Get<T>(string property, object value) : where T is Account
{
//p
var p = Expression.Parameter(typeof(T));
//p.Property
var propertyExpression = Expression.Property(p, property);
//p.Property == value
var equalsExpression = Expression.Equal(propertyExpression, Expression.Constant(value));
//p => p.Property == value
var lambda = Expression.Lambda<Func<T,bool>>(equalsExpression, p);
return context.Set<T>().SingleOrDefault(lambda);
}
It uses EF 5's Set<T>() method. If you are using a lower version, you'll need to implement a way of getting the DbSet based on the <T> type.
Hope it helps.
Dynamic Linq may be an option. Specify your criteria as a string and it will get built as an expression and ran against your data;
An example from something I have done;
var context = new DataContext(ConfigurationManager.ConnectionStrings["c"].ConnectionString);
var statusConditions = "Status = 1";
var results = (IQueryable)context.Contacts.Where(statusConditions);
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Resources