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.
Related
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();
}
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 have the below list which consist of a list of names and scores. The scores I got from using linq to get the sum of points for the list of peope:
Name Score
Ed 5
John 6
Sara 7
Dean 3
Nora 4
So i was able to group the list and make the sums, but now I am trying to rank these based on the score. So I am trying to get the below list:
Rank Name Score
1 Sara 7
2 John 6
3 Ed 5
4 Nora 4
5 Dean 3
But the linq code I am using doesn't seem to be getting the right ranks for me.
Here's my code for making the list and making the sums and ranking:
public class Person
{
public int Rank { get; set; }
public string Name { get; private set; }
public int Score { get; set; }
public Person(int rank, string name, int score)
{
Rank = rank;
Name = name;
Score = score;
}
}
public ObservableCollection<Person> Persons { get; set; }
public MainWindow()
{
Persons = new ObservableCollection<Person>();
var data = lines.Select(line => {
var column = line.Split(',');
var name = column[0];
int score = int.Parse(column[1]);
int rank =0;
return new { name, score, rank };
});
var groupedData = data.OrderByDescending(x => x.score).GroupBy(p => p.name).Select((g, i) => new { name = g.Key, rank=i+1, score = g.Sum(p => p.score)});
var persons = groupedData.Select(p => new Person(p.rank, p.name, p.score));
foreach (var person in persons) {
Persons.Add(person);
}
datagrid.ItemsSource = Persons;
}
I just would like to know how to include correct ranks in linq.
You have to OrderByDescending the groupedData (which has a total score) rather than the raw data containing non-summed, individual scores.
var persons = groupedData.Select(p => new Person(p.rank, p.name, p.score)).OrderByDescending(x => x.Score);
Edit: (putting the correct rank into Person.Rank)
var groupedData = data.GroupBy(p => p.name).Select(g => new { name = g.Key, score = g.Sum(p => p.score)}).OrderByDescending(x => x.score);
var persons = groupedData.Select((p,i) => new Person(i+1, p.name, p.score));
I asked a similar question to this a while back. My problem was that if people have the same score it will assign the wrong rank to the person. With your current code if John also had a score of 7 he would be ranked #2 as you are ordering by score and just using its position in the list when hes really tied for #1.
Here was my question and solution: Get the index of item in list based on value
How about an extension method doing ranking:
public static IEnumerable<TResult> SelectRank<TSource, TValue, TResult>(this IEnumerable<TSource> source,
Func<TSource, TValue> valueSelector,
Func<TSource, int, TResult> selector)
where TValue : struct
{
TValue? previousValue = default(TValue?);
var count = 0;
var rank = 0;
foreach (var item in source)
{
count++;
var value = valueSelector(item);
if (!value.Equals(previousValue))
{
rank = count;
previousValue = value;
}
yield return selector(item, rank);
}
}
Usage profiles.SelectRank(x => x.Score, (x, r) => new { x.Name, Rank = r }). This method do only 1 iteration of the source. I do recommend to use a sorted source parameter.
Adding code here for completion as tournament example:
var entriesByPoints = tournament.Entries.GroupBy(g=>g.Picks.Sum(s=>s.TournamentContestant.TotalScoreTargetAllocatedPoints)).Select((group)=> new {
Points = group.Key,
Entries = group.ToList()
});
var entriesByRankWithPoints = entriesByPoints.OrderByDescending(ob=>ob.Points).Select((p,i) => new {
Rank = 1 + (entriesByPoints.Where(w=>w.Points > p.Points).Sum(s=>s.Entries.Count())),
Points = p.Points,
Entries = p.Entries
});
Results:
Assuming
public class MyClass
{
public int ID {get; set; }
public string Name {get; set; }
}
and
List<MyClass> classList = //populate with MyClass instances of various IDs
I can do
List<MyClass> result = classList.FindAll(class => class.ID == 123);
and that will give me a list of just classes with ID = 123. Works great, looks elegant.
Now, if I had
List<List<MyClass>> listOfClassLists = //populate with Lists of MyClass instances
How do I get a filtered list where the lists themselves are filtered. I tried
List<List<MyClass>> result = listOfClassLists.FindAll
(list => list.FindAll(class => class.ID == 123).Count > 0);
it looks elegant, but doesn't work. It only includes Lists of classes where at least one class has an ID of 123, but it includes ALL MyClass instances in that list, not just the ones that match.
I ended up having to do
List<List<MyClass>> result = Results(listOfClassLists, 123);
private List<List<MyClass>> Results(List<List<MyClass>> myListOfLists, int id)
{
List<List<MyClass>> results = new List<List<MyClass>>();
foreach (List<MyClass> myClassList in myListOfLists)
{
List<MyClass> subList = myClassList.FindAll(myClass => myClass.ID == id);
if (subList.Count > 0)
results.Add(subList);
}
return results;
}
which gets the job done, but isn't that elegant. Just looking for better ways to do a FindAll on a List of Lists.
Ken
listOfClasses.SelectMany(x=>x).FindAll( /* yadda */)
Sorry about that, FindAll is a method of List<T>.
This
var result = from x in listOfClasses from y in x where SomeCondition(y) select y;
or
var result = listOfClasses.SelectMany(x=>x).Where(x=>SomeCondition(x));
To keep a list of lists, you could do something like this example:
MyClass a = new MyClass() { ID = 123, Name = "Apple" };
MyClass b = new MyClass() { ID = 456, Name = "Banana" };
MyClass c = new MyClass() { ID = 789, Name = "Cherry" };
MyClass d = new MyClass() { ID = 123, Name = "Alpha" };
MyClass e = new MyClass() { ID = 456, Name = "Bravo" };
List<List<MyClass>> lists = new List<List<MyClass>>()
{
new List<MyClass>() { a, b, c },
new List<MyClass>() { d, e },
new List<MyClass>() { b, c, e}
};
var query = lists
.Select(list => list.Where(item => item.ID == 123).ToList())
.Where(list => list.Count > 0).ToList();
query would be List<List<MyClass>> holding lists of MyClass objects that passed the test. At first glance, it looks out of order with the Where extension coming after the Select, but the transformation of the inner lists needs to occur first, and that's what's happening in the Select extension. Then it is filtered by the Where.
I would probably go with this
List<List<string>> stuff = new List<List<string>>();
List<List<string>> results = new List<List<string>>();
stuff.ForEach(list=> {var result = list.FindAll(i => i == "fun").ToList();
if (result.Count > 0) results.Add(result);
});
List<string> flatResult = new List<string>();
stuff.ForEach(List => flatResult.AddRange(List.FindAll(i => i == "fun")));
That way you can go with a jagged array or flatten it out.. But the Linq way works well too :-).
While producing a flat List<MyClass> will answer your need most of the time, the exact answer to your question is:
var result = (from list in ListOfClassLists
let listWithTheId=
(
(from myClass in list
where myClass.ID == id
select myClass)
.ToList()
)
where listWithTheId.Count > 0
select listWithTheId
).ToList();
This code snippet was taken from my Proof of Concept:
using System.Collections.Generic;
using System.Linq;
namespace ListOfListSelectionSpike
{
public class ListSpikeClass
{
public List<List<MyClass>> ListOfClassLists { get; set; }
private List<MyClass> list1, list2, list3;
public ListSpikeClass()
{
var myClassWithId123 = new MyClass("123");
var myClassWithIs345 = new MyClass("456");
list1 = new List<MyClass> { myClassWithId123, myClassWithIs345 };
list2 = new List<MyClass> { myClassWithId123, myClassWithIs345, myClassWithId123 };
list3 = new List<MyClass> { myClassWithIs345, myClassWithIs345 };
ListOfClassLists = new List<List<MyClass>> { list1, list2, list3 };
}
public List<List<MyClass>> GetListOfListsById(string id)
{
var result = (from list in ListOfClassLists
let listWithTheId =
((from myClass in list
where myClass.ID == id
select myClass)
.ToList())
where listWithTheId.Count > 0
select listWithTheId)
.ToList();
return result;
}
}
public class MyClass
{
public MyClass(string id)
{
ID = id;
Name = "My ID=" + id;
}
public string ID { get; set; }
public string Name { get; set; }
}
}
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; }
}