简体   繁体   中英

How to remove Distinct values from a list?

I want to remove only distinct values from a list using LINQ.

My code-

List<MyStudents> studentsList = new List<MyStudents>();

public class MyStudents
{
     private string student;
     private int age;

     public string Student
     {
         get { return student; }
         set { student = value; }
     }

     public int Age
     {
         get { return age; }
         set { age = value; }
     }
}

I tried-

studentsList.RemoveAll(o => o.Student).Distinct();

But this doesn't work and displays an error-

Cannot convert Lambda expression to intended delegate because some of the return types in the block are not implicitly convertible to the delegate return type

PN: I prefer removing distinct items from existing list than creating an a new list with distinct values

If you've implemented Equals and GetHashCode for Student , you can try Linq GroupBy followed by SelectMany :

 studentsList = studentsList
   .GroupBy(o => o.Student)            // Groups of same students
   .Where(group => group.Count() >= 2) // Students appear at least twice
   .SelectMany(group => group)         // Ungroup (flatten) to IEnumerable<Student> 
   .ToList();                          // Materialized as List

If you haven't implemented Equals and GetHashCode you have to invent the grouping key, eg:

 studentsList = studentsList
   .GroupBy(o => new { // Student are equal if and only if they have same
      o.Student.Name,  // Name 
      o.Student.Age,   // and Age
    }) 
   .Where(group => group.Count() >= 2) 
   .SelectMany(group => group)
   .ToList();

In this case, I would suggest you use a HashSet .

Remember to always try and match your data structure to the problem you're trying to solve. Consider this, would you try and put a screw in your wall with a hammer? Sure, it's possible, but not optimal.

If you're hard stuck on using LINQ and Lists to solve this problem, you can just call.Distinct() on your list and it will return the distinct elements.

First take all values that appear at least twice:

var atLeastTwicePresent = studentsList
    .Select(sObj => sObj.Student)
    .Where(s => studentsList.Where(sObj => sObj.Student == s).Count() > 1);

Then take distinct values from this set:

var distinctAtLeastTwice = atLeastTwicePresent.Distinct().ToList();

And you should have the Distinct Student names that appear at least twice in your list.

If you want to have only the students that occur at least twice in your result, try grouping them and filter the groups based on the count:

studentsList.GroupBy(s => s.Student).Where(g => g.Count() > 1).Select(g => g.First())

or

from s in studentsList
group s by s.Student into g
where g.Count() > 1
select g.First()

You need to group the items by the key you want. With a new { } you could create a key using the linq group. Then, instead of selecting the key (which is an anonymous class, which you don't want) you select the first in the group.

The idea of an anonymous class is, that the group compares all properties, so it can be group. (that's why you can't group on the MyStudents class)

You could try this:

using System;
using System.Collections.Generic;
using System.Linq;

public class MyStudents
{
     private string student;
     private int age;

     public string Student
     {
         get { return student; }
         set { student = value; }
     }

     public int Age
     {
         get { return age; }
         set { age = value; }
     }
}

public class Program
{
    public static void Main()
    {
        // some testdata...
        var students = new List<MyStudents>
        {
            new MyStudents { Student = "John", Age = 17 },
            new MyStudents { Student = "Mary", Age = 18 },
            new MyStudents { Student = "John", Age = 17 },
            new MyStudents { Student = "Hank", Age = 20 },
        };


        var newList = students

           // The whole idea is, group the list by creating a key.
           .GroupBy(o => new {o.Student, o.Age})
           // you don't want to select the key, because it's an anonymous class
           // So select the first item which was added to the group.
           .Select(o => o.First())
           // create a new list.
           .ToList();

        // present them         
        foreach(var s in newList)
            Console.WriteLine("Student: "+s.Student+", Age: "+s.Age);
    }
}}

Here is a dotnetfiddle

Here is a example: use list.Distinct().ToList();

// List with duplicate elements.
        List<int> list = new List<int>();
        list.Add(1);
        list.Add(2);
        list.Add(3);
        list.Add(3);
        list.Add(4);
        list.Add(4);
        list.Add(4);

        foreach (int value in list)
        {
            Console.WriteLine("Before: {0}", value);
        }

        // Get distinct elements and convert into a list again.
        List<int> distinct = list.Distinct().ToList();

        foreach (int value in distinct)
        {
            Console.WriteLine("After: {0}", value);
        }
    }

dotnetfiddle


Using.destinct for classes makes a comparer-method necessary: see here. but it have to be a instance of IEqualityComparer !

class ProductComparer : IEqualityComparer<Product>  
{
            public bool Equals(Product x, Product y) {
                if (Object.ReferenceEquals(x, y)) return true;

                if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
                    return false;

                return x.Make == y.Make && x.Model == y.Model;
            }
            public int GetHashCode(Product product) {
                if (Object.ReferenceEquals(product, null)) return 0;
                int hashProductName = product.Make == null ? 0 : product.Make.GetHashCode();
                int hashProductCode = product.Model.GetHashCode();
                return hashProductName ^ hashProductCode;
            }
 }

**The Distinct operator has an overload method that lets you pass an instance of IEqualityComparer.** So for this approach we created a class “ProductComparer” that implements the IEqualityCompaper. Here’s the code to use it:


if (!IsPostBack) {  
                GridView1.DataSource = GetProducts()
                                       .Distinct(new ProductComparer());
                GridView1.DataBind();
}

If you want to get all the unique Student, then you simply need to select distinct from your list.

something like this: studentsList.DistinctBy(x => x.id);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM