I have list of data and user defined sort list and I need to sort my source list based on total numbers defined in the sort list object. It should be dynamic so that the user can freely create his own sort list preference. In my sample code I used Person class. The class may have more properties in the future that's why I want that my sort expression is dynamic too. I used PropertyName to convey a lookup for property. In my example below I have list of person and I have list of sort preference. In my first example I want to sort the person list by Name ascending, then by Age descending. Can someone help me have a LINQ extension? I saw an example in this post Dynamic Linq Order By Variable
The scenario in that post is quite similar to mine except this one is using fixed properties. What I want to achieve is dynamic like the following.
Sort expression is dynamic that is I need to look up for property name that has matching in my sort expression. If any found sort based on sort direction.
Sort execution should be based on how many sort items are defined in the sort list. For example loop through the sort list and do OrderBy (if ascending), OrderByDescending (if descending), ThenBy, ThenBy so on and so fort. For example I have 2 sort order then the source list should be ordered by then by. If I have 5 then the list should sorted in 1 "OrderBy (desc or asc)" and 4 "ThenBy (desc or asc)". The chain should not be broken that for example given 4 sort order and all are ascending it will become persons.OrderBy(prop1).ThenBy(prop2).ThenBy(prop3).ThenBy(prop4).
C# Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SortDemo
{
class Program
{
static void Main(string[] args)
{
var persons = new List<Person>();
persons.Add(new Person { Name="George", Age=25, Group="A", State="LA"});
persons.Add(new Person { Name = "Anna", Age = 20, Group = "B", State = "CA" });
persons.Add(new Person { Name = "Xenna", Age = 30, Group = "A", State = "DC" });
persons.Add(new Person { Name = "Sam", Age = 40, Group = "C", State = "IL" });
persons.Add(new Person { Name = "Elise", Age = 21, Group = "B", State = "LA" });
persons.Add(new Person { Name = "Josh", Age = 29, Group = "C", State = "MI" });
persons.Add(new Person { Name = "Mike", Age = 34, Group = "A", State = "NY" });
persons.Add(new Person { Name = "Bernard", Age = 27, Group = "C", State = "WY" });
var sorts = new List<Sort>();
sorts.Add(new Sort { PropertyName = "Age", SortOrder = 2, Direction = "Descending" });
sorts.Add(new Sort { PropertyName="Name", SortOrder=1, Direction="Ascending"});
//sort by two properties
foreach(var sort in sorts.OrderBy(x=>x.SortOrder))
{
//OrderBy if sort direction is Ascending
//OrderByDescending if sort direction is Descending
var sortedPersons = persons.OrderBy(x=>PropertyName==sort.PropertyName);
//expected results
//order persons by Name ascending
//then by Age Descending
}
//another example
var sorts1 = new List<Sort>();
sorts1.Add(new Sort { PropertyName = "Name", SortOrder = 4, Direction = "Descending" });
sorts1.Add(new Sort { PropertyName = "Age", SortOrder = 1, Direction = "Ascending" });
sorts1.Add(new Sort { PropertyName = "State", SortOrder = 3, Direction = "Ascending" });
sorts1.Add(new Sort { PropertyName = "Group", SortOrder = 2, Direction = "Ascending" });
//sort by four properties
foreach (var sort in sorts1.OrderBy(x => x.SortOrder))
{
//OrderBy if sort direction is Ascending
//OrderByDescending if sort direction is Descending
var sortedPersons1 = persons.OrderBy(x => PropertyName == sort.PropertyName);
//expected results
//order persons by Age Ascending
//then by Group Ascending
//then by State Ascending
//then by Name Descending
}
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Group { get; set; }
public string State { get; set; }
}
public class Sort
{
public string PropertyName { get; set; }
public int SortOrder { get; set; }
public string Direction { get; set; }
}
}
This is my original answer which has IQueryable implementation - good for LINQ to Entities queries, but also can be performant for LINQ to Objects.
In your case it can be used in this way:
var sorts = new List<Sort>();
sorts.Add(new Sort { PropertyName = "Age", SortOrder = 2, Direction = "Descending" });
sorts.Add(new Sort { PropertyName = "Name", SortOrder = 1, Direction = "Ascending"});
var orderByInfo = sorts.OrderBy(s => s.SortOrder)
.Select(s => Tuple.Create(s.PropertyName, s.Direction == "Descending"));
var sordedPersons = persons.AsQueryable().ApplyOrderBy(orderByInfo).ToList();
Try this to sort an in-memory list.
List<Person> SortDynamically(IEnumerable<Person> persons, IList<Sort> sorts)
{
// this line to get an IOrderedEnumerable<T> so that we can chain ThenBy(s)
var sortedPersons = persons.OrderBy(x => 1);
foreach(var sort in sorts.OrderBy(x => x.SortOrder))
{
sortedPersons = sort.Direction switch
{
"Ascending" => sortedPersons
.ThenBy(x => x.GetType().GetProperty(sort.PropertyName)?.GetValue(x, null)),
"Descending" => sortedPersons
.ThenByDescending(x => x.GetType().GetProperty(sort.PropertyName)?.GetValue(x, null)),
_ => throw new ArgumentException("Sort Direction must be Ascending or Descending")
};
}
return sortedPersons.ToList();
}
Alternatively, if you do not like the persons.OrderBy(x => 1) trick, you could the call OrderBy and ThenBy separately.
List<Person> SortDynamicallyAlt(IEnumerable<Person> persons, IList<Sort> sorts)
{
if(sorts.Count == 0)
{
return persons.ToList();
}
var firstSort = sorts.OrderBy(x => x.SortOrder).First();
var sortedPersonsAlt = firstSort.Direction switch
{
"Ascending" => persons
.OrderBy(x => x.GetType().GetProperty(firstSort.PropertyName)?.GetValue(x, null)),
"Descending" => persons
.OrderByDescending(x => x.GetType().GetProperty(firstSort.PropertyName)?.GetValue(x, null)),
_=> throw new ArgumentException("Sort Direction must be Ascending or Descending")
};
foreach(var sort in sorts.OrderBy(x => x.SortOrder).Skip(1))
{
sortedPersonsAlt = sort.Direction switch
{
"Ascending" => sortedPersonsAlt
.ThenBy(x => x.GetType().GetProperty(sort.PropertyName)?.GetValue(x, null)),
"Descending" => sortedPersonsAlt
.ThenByDescending(x => x.GetType().GetProperty(sort.PropertyName)?.GetValue(x, null)),
_=> throw new ArgumentException("sort Direction must be Ascending or Descending")
};
}
return sortedPersonsAlt.ToList();
}
Related
The best way I can describe what I'm trying to do is "Nested DistinctBy".
Let's say I have a collection of objects. Each object contains a collection of nicknames.
class Person
{
public string Name { get; set; }
public int Priority { get; set; }
public string[] Nicknames { get; set; }
}
public class Program
{
public static void Main()
{
var People = new List<Person>
{
new Person { Name = "Steve", Priority = 4, Nicknames = new string[] { "Stevo", "Lefty", "Slim" }},
new Person { Name = "Karen", Priority = 6, Nicknames = new string[] { "Kary", "Birdie", "Snookie" }},
new Person { Name = "Molly", Priority = 3, Nicknames = new string[] { "Mol", "Lefty", "Dixie" }},
new Person { Name = "Greg", Priority = 5, Nicknames = new string[] { "G-man", "Chubs", "Skippy" }}
};
}
}
I want to select all Persons but make sure nobody selected shares a nickname with another. Molly and Steve both share the nickname 'Lefty' so I want to filter one of them out. Only the one with highest priority should be included. If there is a highest priority tie between 2 or more then just pick the first one of them. So in this example I would want an IEnumerable of all people except Steve.
EDIT: Here's another example using music album instead of person, might make more sense.
class Album
{
string Name {get; set;}
int Priority {get;set;}
string[] Aliases {get; set;}
{
class Program
{
var NeilYoungAlbums = new List<Album>
{
new Person{ Name = "Harvest (Remastered)", Priority = 4, Aliases = new string[] { "Harvest (1972)", "Harvest (2012)"}},
new Person{ Name = "On The Beach", Priority = 6, Aliases = new string[] { "The Beach Album", "On The Beach (1974)"}},
new Person{ Name = "Harvest", Priority = 3, Aliases = new string[] { "Harvest (1972)"}},
new Person{ Name = "Freedom", Priority = 5, Aliases = new string[] { "Freedom (1989)"}}
};
}
The idea here is we want to show his discography but we want to skip quasi-duplicates.
I would solve this using a custom IEqualityComparer<T>:
class Person
{
public string Name { get; set; }
public int Priority { get; set; }
public string[] Nicknames { get; set; }
}
class PersonEqualityComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null || y == null) return false;
return x.Nicknames.Any(i => y.Nicknames.Any(j => i == j));
}
// This is bad for performance, but if performance is not a
// concern, it allows for more readability of the LINQ below
// However you should check the Edit, if you want a truely
// LINQ only solution, without a wonky implementation of GetHashCode
public int GetHashCode(Person obj) => 0;
}
// ...
var people = new List<Person>
{
new Person { Name = "Steve", Priority = 4, Nicknames = new[] { "Stevo", "Lefty", "Slim" } },
new Person { Name = "Karen", Priority = 6, Nicknames = new[] { "Kary", "Birdie", "Snookie" } },
new Person { Name = "Molly", Priority = 3, Nicknames = new[] { "Mol", "Lefty", "Dixie" } },
new Person { Name = "Greg", Priority = 5, Nicknames = new[] { "G-man", "Chubs", "Skippy" } }
};
var distinctPeople = people.OrderBy(i => i.Priority).Distinct(new PersonEqualityComparer());
EDIT:
Just for completeness, this could be a possible LINQ only approach:
var personNicknames = people.SelectMany(person => person.Nicknames
.Select(nickname => new { person, nickname }));
var groupedPersonNicknames = personNicknames.GroupBy(i => i.nickname);
var duplicatePeople = groupedPersonNicknames.SelectMany(i =>
i.OrderBy(j => j.person.Priority)
.Skip(1).Select(j => j.person)
);
var distinctPeople = people.Except(duplicatePeople);
A LINQ-only solution
var dupeQuery = people
.SelectMany( p => p.Nicknames.Select( n => new { Nickname = n, Person = p } ) )
.ToLookup( e => e.Nickname, e => e.Person )
.SelectMany( e => e.OrderBy( p => p.Priority ).Skip( 1 ) );
var result = people.Except( dupeQuery ).ToList();
See .net fiddle sample
This works once, then you have to clear the set. Or store the results in a collection.
var uniqueNicknames = new HashSet<string>();
IEnumerable<Person> uniquePeople = people
.OrderBy(T => T.Priority) // ByDescending?
.Where(T => T.Nicknames.All(N => !uniqueNicknames.Contains(N)))
.Where(T => T.Nicknames.All(N => uniqueNicknames.Add(N)));
I need to remove elements in a single list considering one or more duplicated subelement
Classes
public class Person
{
public int id { get; set; }
public string name { get; set; }
public List<IdentificationDocument> documents { get; set; }
public Person()
{
documents = new List<IdentificationDocument>();
}
}
public class IdentificationDocument
{
public string number { get; set; }
}
Code:
var person1 = new Person() {id = 1, name = "Bob" };
var person2 = new Person() {id = 2, name = "Ted" };
var person3 = new Person() {id = 3, name = "Will_1" };
var person4 = new Person() {id = 4, name = "Will_2" };
person1.documents.Add(new IdentificationDocument() { number = "123" });
person2.documents.Add(new IdentificationDocument() { number = "456" });
person3.documents.Add(new IdentificationDocument() { number = "789" });
person4.documents.Add(new IdentificationDocument() { number = "789" }); //duplicate
var personList1 = new List<Person>();
personList1.Add(person1);
personList1.Add(person2);
personList1.Add(person3);
personList1.Add(person4);
//more data for performance test
for (int i = 0; i < 20000; i++)
{
var personx = new Person() { id = i, name = Guid.NewGuid().ToString() };
personx.documents.Add(new IdentificationDocument() { number = Guid.NewGuid().ToString() });
personx.documents.Add(new IdentificationDocument() { number = Guid.NewGuid().ToString() });
personList1.Add(personx);
}
var result = //Here comes the linq query
result.ForEach(r => Console.WriteLine(r.id + " " +r.name));
Expected result:
1 Bob
2 Ted
3 Will_1
Example
https://dotnetfiddle.net/LbPLcP
Thank you!
You can use the Enumerable.Distinct<TSource> method from LINQ. You'll need to create a custom comparer to compare using the subelement.
See How do I use a custom comparer with the Linq Distinct method?
Well, yes, you could use a custom comparer. But that's going to be lots more code than your specific example requires. If your specific example is all you need, this this will work fine:
var personDocumentPairs = personList1
.SelectMany(e => e.documents.Select(t => new {person = e, document = t}))
.GroupBy(e => e.document.number).Select(e => e.First());
var result = personDocumentPairs.Select(e => e.person).Distinct();
along the lines of Adam's solution the trick is to iterate persons and group them by associated document numbers.
// persons with already assigned documents
// Will_2
var duplicate = from person in personList1
from document in person.documents
group person by document.number into groupings
let counter = groupings.Count()
where counter > 1
from person in groupings
.OrderBy(p => p.id)
.Skip(1)
select person;
// persons without already assigned documents
// Bob
// Ted
// Will_1
var distinct = from person in personList1
from document in person.documents
group person by document.number into groupings
from person in groupings
.OrderBy(p => p.id)
.Take(1)
select person;
the orderby is a made up rule for the already assigned documents persons, but your mileage may vary
I would assume there's a simple LINQ query to do this, I'm just not exactly sure how. Please see code snippet below, the comment explains what I'd like to do:
class Program
{
static void Main(string[] args)
{
List<Person> peopleList1 = new List<Person>();
peopleList1.Add(new Person() { ID = 1 });
peopleList1.Add(new Person() { ID = 2 });
peopleList1.Add(new Person() { ID = 3 });
peopleList1.Add(new Person() { ID = 4});
peopleList1.Add(new Person() { ID = 5});
List<Person> peopleList2 = new List<Person>();
peopleList2.Add(new Person() { ID = 1 });
peopleList2.Add(new Person() { ID = 4});
//I would like to perform a LINQ query to give me only
//those people in 'peopleList1' that are in 'peopleList2'
//this example should give me two people (ID = 1& ID = 4)
}
}
class Person
{
public int ID { get; set; }
}
var result = peopleList2.Where(p => peopleList1.Any(p2 => p2.ID == p.ID));
You can do this using Where:
var result = peopleList1.Where(p => peopleList2.Any(p2 => p2.ID == p.ID));
You can also use Intersect (var result = peopleList1.Intersect(peopleList2);), but that would need you to implement an extra IEqualityComparer<Person> or override Person's Equals and GetHashCode methods in a way that two Person instances with the same ID are regarded equal. Intersect would otherwise perform reference equality.
I would join both lists on ID:
var inboth = from p1 in peopleList1
join p2 in peopleList2
on p1.ID equals p2.ID
select p1;
List<Person> joinedList = inboth.ToList();
Related: Why is LINQ JOIN so much faster than linking with WHERE?
If you would override Equals + GetHashCode you could use Intersect:
List<Person> joinedList = peopleList1.Intersect(peopleList2).ToList();
or you could provide a custom IEqualityComparer<Person> for Intersect:
public class PersonIdComparer: IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if(object.ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.ID == y.ID;
}
public int GetHashCode(Person obj)
{
return obj == null ? int.MinValue : obj.ID;
}
}
Now you can use it in this way:
List<Person> joinedList = peopleList1
.Intersect(peopleList2, new PersonIdComparer())
.ToList();
Both, Enumerable.Join and Enumerable.Intersect are efficient since they are using a set.
Product[] fruits1 = { new Product { Name = "apple", Code = 9 },
new Product { Name = "orange", Code = 4 },
new Product { Name = "lemon", Code = 12 } };
Product[] fruits2 = { new Product { Name = "apple", Code = 9 } };
//Get all the elements from the first array
//except for the elements from the second array.
IEnumerable<Product> except =
fruits1.Except(fruits2);
foreach (var product in except)
Console.WriteLine(product.Name + " " + product.Code);
/*
This code produces the following output:
orange 4
lemon 12
*/
You can use Enumerable.Intersect
var common = peopleList1.Intersect(peopleList2);
You just can use the LINQ Intersect Extension Method.
http://msdn.microsoft.com/en-us/library/bb460136(v=VS.100).aspx
So you would do it like that:
class Program
{
static void Main(string[] args)
{
List<Person> peopleList1 = new List<Person>();
peopleList1.Add(new Person() { ID = 1 });
peopleList1.Add(new Person() { ID = 2 });
peopleList1.Add(new Person() { ID = 3 });
peopleList1.Add(new Person() { ID = 4});
peopleList1.Add(new Person() { ID = 5});
List<Person> peopleList2 = new List<Person>();
peopleList2.Add(new Person() { ID = 1 });
peopleList2.Add(new Person() { ID = 4});
var result = peopleList1.Intersect(peopleList2);
}
}
Just override the Equals Method in the Person-Class. I think you could compare the ids there.
I have 3 List containing : Index, Name, Age
Example:
List<int> indexList = new List<int>();
indexList.Add(3);
indexList.Add(1);
indexList.Add(2);
List<string> nameList = new List<string>();
nameList.Add("John");
nameList.Add("Mary");
nameList.Add("Jane");
List<int> ageList = new List<int>();
ageList.Add(16);
ageList.Add(17);
ageList.Add(18);
I now have to sort all 3 list based on the indexList.
How do I use .sort() for indexList while sorting the other 2 list as well
You are looking at it the wrong way. Create a custom class:
class Person
{
public int Index { get; set; }
public string Name{ get; set; }
public int Age{ get; set; }
}
Then, sort the List<Person> with the help of the OrderBy method from the System.Linq namespace:
List<Person> myList = new List<Person>() {
new Person { Index = 1, Name = "John", Age = 16 };
new Person { Index = 2, Name = "James", Age = 19 };
}
...
var ordered = myList.OrderBy(x => x.Index);
Also, you can read Jon Skeet article about your anti-pattern.
Farhad's answer is correct and should be accepted. But if you really have to sort three related lists in that way you could use Enumerable.Zip and OrderBy:
var joined = indexList.Zip(
nameList.Zip(ageList, (n, a) => new { Name = n, Age = a }),
(ix, x) => new { Index = ix, x.Age, x.Name })
.OrderBy(x => x.Index);
indexList = joined.Select(x => x.Index).ToList();
nameList = joined.Select(x => x.Name).ToList();
ageList = joined.Select(x => x.Age).ToList();
All are ordered by the value in the index-list afterwards.
I have a class Items with properties (Id, Name, Code, Price).
The List of Items is populated with duplicated items.
For ex.:
1 Item1 IT00001 $100
2 Item2 IT00002 $200
3 Item3 IT00003 $150
1 Item1 IT00001 $100
3 Item3 IT00003 $150
How to remove the duplicates in the list using linq?
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());
var distinctItems = items.Distinct();
To match on only some of the properties, create a custom equality comparer, e.g.:
class DistinctItemComparer : IEqualityComparer<Item> {
public bool Equals(Item x, Item y) {
return x.Id == y.Id &&
x.Name == y.Name &&
x.Code == y.Code &&
x.Price == y.Price;
}
public int GetHashCode(Item obj) {
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode() ^
obj.Code.GetHashCode() ^
obj.Price.GetHashCode();
}
}
Then use it like this:
var distinctItems = items.Distinct(new DistinctItemComparer());
If there is something that is throwing off your Distinct query, you might want to look at MoreLinq and use the DistinctBy operator and select distinct objects by id.
var distinct = items.DistinctBy( i => i.Id );
This is how I was able to group by with Linq. Hope it helps.
var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());
An universal extension method:
public static class EnumerableExtensions
{
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
{
return enumerable.GroupBy(keySelector).Select(grp => grp.First());
}
}
Example of usage:
var lstDst = lst.DistinctBy(item => item.Key);
You have three option here for removing duplicate item in your List:
Use a a custom equality comparer and then use Distinct(new DistinctItemComparer()) as #Christian Hayter mentioned.
Use GroupBy, but please note in GroupBy you should Group by all of the columns because if you just group by Id it doesn't remove duplicate items always. For example consider the following example:
List<Item> a = new List<Item>
{
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
new Item {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}
};
var distinctItems = a.GroupBy(x => x.Id).Select(y => y.First());
The result for this grouping will be:
{Id = 1, Name = "Item1", Code = "IT00001", Price = 100}
{Id = 2, Name = "Item2", Code = "IT00002", Price = 200}
{Id = 3, Name = "Item3", Code = "IT00003", Price = 150}
Which is incorrect because it considers {Id = 3, Name = "Item3", Code = "IT00004", Price = 250} as duplicate. So the correct query would be:
var distinctItems = a.GroupBy(c => new { c.Id , c.Name , c.Code , c.Price})
.Select(c => c.First()).ToList();
3.Override Equal and GetHashCode in item class:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int Price { get; set; }
public override bool Equals(object obj)
{
if (!(obj is Item))
return false;
Item p = (Item)obj;
return (p.Id == Id && p.Name == Name && p.Code == Code && p.Price == Price);
}
public override int GetHashCode()
{
return String.Format("{0}|{1}|{2}|{3}", Id, Name, Code, Price).GetHashCode();
}
}
Then you can use it like this:
var distinctItems = a.Distinct();
Use Distinct() but keep in mind that it uses the default equality comparer to compare values, so if you want anything beyond that you need to implement your own comparer.
Please see http://msdn.microsoft.com/en-us/library/bb348436.aspx for an example.
Try this extension method out. Hopefully this could help.
public static class DistinctHelper
{
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var identifiedKeys = new HashSet<TKey>();
return source.Where(element => identifiedKeys.Add(keySelector(element)));
}
}
Usage:
var outputList = sourceList.DistinctBy(x => x.TargetProperty);
List<Employee> employees = new List<Employee>()
{
new Employee{Id =1,Name="AAAAA"}
, new Employee{Id =2,Name="BBBBB"}
, new Employee{Id =3,Name="AAAAA"}
, new Employee{Id =4,Name="CCCCC"}
, new Employee{Id =5,Name="AAAAA"}
};
List<Employee> duplicateEmployees = employees.Except(employees.GroupBy(i => i.Name)
.Select(ss => ss.FirstOrDefault()))
.ToList();
Another workaround, not beautiful buy workable.
I have an XML file with an element called "MEMDES" with two attribute as "GRADE" and "SPD" to record the RAM module information.
There are lot of dupelicate items in SPD.
So here is the code I use to remove the dupelicated items:
IEnumerable<XElement> MList =
from RAMList in PREF.Descendants("MEMDES")
where (string)RAMList.Attribute("GRADE") == "DDR4"
select RAMList;
List<string> sellist = new List<string>();
foreach (var MEMList in MList)
{
sellist.Add((string)MEMList.Attribute("SPD").Value);
}
foreach (string slist in sellist.Distinct())
{
comboBox1.Items.Add(slist);
}
When you don't want to write IEqualityComparer you can try something like following.
class Program
{
private static void Main(string[] args)
{
var items = new List<Item>();
items.Add(new Item {Id = 1, Name = "Item1"});
items.Add(new Item {Id = 2, Name = "Item2"});
items.Add(new Item {Id = 3, Name = "Item3"});
//Duplicate item
items.Add(new Item {Id = 4, Name = "Item4"});
//Duplicate item
items.Add(new Item {Id = 2, Name = "Item2"});
items.Add(new Item {Id = 3, Name = "Item3"});
var res = items.Select(i => new {i.Id, i.Name})
.Distinct().Select(x => new Item {Id = x.Id, Name = x.Name}).ToList();
// now res contains distinct records
}
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}