Does C# Distinct() method keep original ordering of sequence intact?

后端 未结 6 817
情话喂你
情话喂你 2020-11-29 11:01

I want to remove duplicates from list, without changing order of unique elements in the list.

Jon Skeet & others have suggested to use following

         


        
6条回答
  •  抹茶落季
    2020-11-29 11:34

    By default when use Distinct linq operator uses Equals method but you can use your own IEqualityComparer object to specify when two objects are equals with a custom logic implementing GetHashCode and Equals method. Remember that:

    GetHashCode should not used heavy cpu comparision ( eg. use only some obvious basic checks ) and its used as first to state if two object are surely different ( if different hash code are returned ) or potentially the same ( same hash code ). In this latest case when two object have the same hashcode the framework will step to check using the Equals method as a final decision about equality of given objects.

    After you have MyType and a MyTypeEqualityComparer classes follow code not ensure the sequence maintain its order:

    var cmp = new MyTypeEqualityComparer();
    var lst = new List();
    // add some to lst
    var q = lst.Distinct(cmp);
    

    In follow sci library I implemented an extension method to ensure Vector3D set maintain the order when use a specific extension method DistinctKeepOrder:

    relevant code follows:

    /// 
    /// support class for DistinctKeepOrder extension
    /// 
    public class Vector3DWithOrder
    {
        public int Order { get; private set; }
        public Vector3D Vector { get; private set; }
        public Vector3DWithOrder(Vector3D v, int order)
        {
            Vector = v;
            Order = order;
        }
    }
    
    public class Vector3DWithOrderEqualityComparer : IEqualityComparer
    {
        Vector3DEqualityComparer cmp;
    
        public Vector3DWithOrderEqualityComparer(Vector3DEqualityComparer _cmp)
        {
            cmp = _cmp;
        }
    
        public bool Equals(Vector3DWithOrder x, Vector3DWithOrder y)
        {
            return cmp.Equals(x.Vector, y.Vector);
        }
    
        public int GetHashCode(Vector3DWithOrder obj)
        {
            return cmp.GetHashCode(obj.Vector);
        }
    }
    

    In short Vector3DWithOrder encapsulate the type and an order integer, while Vector3DWithOrderEqualityComparer encapsulates original type comparer.

    and this is the method helper to ensure order maintained

    /// 
    /// retrieve distinct of given vector set ensuring to maintain given order
    ///         
    public static IEnumerable DistinctKeepOrder(this IEnumerable vectors, Vector3DEqualityComparer cmp)
    {
        var ocmp = new Vector3DWithOrderEqualityComparer(cmp);
    
        return vectors
            .Select((w, i) => new Vector3DWithOrder(w, i))
            .Distinct(ocmp)
            .OrderBy(w => w.Order)
            .Select(w => w.Vector);
    }
    

    Note : further research could allow to find a more general ( uses of interfaces ) and optimized way ( without encapsulate the object ).

提交回复
热议问题