Ryan Hartstein
Ryan Hartstein

Reputation: 43

C# Sorted collection that allows duplicate keys (or values) and that lets me remove items

My basic requirements are:

  1. I need to be able to add objects to a collection, sorted by an object property of my choosing, knowing that the value of this property may occur more than once.
  2. I need to be able to later remove objects from this collection, preferably using the IComparer implementation built into the sorted object.

(I think I'd actually settle for the ability to just add and remove the sorted duplicate values without storing their associated objects, if this makes things any easier.)

Note #1: I've been using the IComparer implementation for duplicate keys as recommended here: C# Sortable collection which allows duplicate keys

However, I run into a problem when I want to remove an object. Since I'm using a custom IComparer object, the dictionary or list fails to find the object by its intrinsic IComparer implementation.

Note #2: I've also tried using the PowerCollections OrderedMultiDictionary class. This didn't bring me much closer to a solution, as I still have to choose between the ability to have duplicate keys (via a custom IComparer implementation) and being able to remove objects from the collection afterwards.

Upvotes: 2

Views: 3444

Answers (2)

D3ivid
D3ivid

Reputation: 61

You cas use Distinct overriding first the equals method, example:

    public void TestMethod1 ( )
    {


        List<Model> listmodel = new List<Model>( )
        {
            new Model() { Prop1 = "one", Prop2 = 2 },
            new Model() { Prop1 = "one", Prop2 = 6 },
            new Model() { Prop1 = "two", Prop2 = 1 },
            new Model() { Prop1 = "three", Prop2 = 7 },
            new Model() { Prop1 = "four", Prop2 = 6 },
        };
        var output = listmodel.Distinct(  ).ToList( );
        output.ToList( ).ForEach( x => Console.WriteLine( x.ToString( ) ) );

    }

    public class Model
    {
        public string Prop1 { get; set; }
        public int Prop2 { get; set; }
        public string Prop3 { get; set; }
        public string Prop4 { get; set; }

        public override bool Equals ( object obj )
        {
            Model casted = obj as Model;
            if (casted == null) return false; //If cast an object to my model is null, is not my model and is not equal
            if (casted.Prop1 == this.Prop1) return true; //My logic define if the input.Prop1 is equal to my Prop1, this is equal
            return false;
        }

        public override int GetHashCode ( )
        {
            return 1;
        }

        public override string ToString ( )
        {
            return $"{this.Prop1} - {this.Prop2} - {this.Prop3} - {this.Prop4}";
        }

    }

Output:

one - 2 -  - 
two - 1 -  - 
three - 7 -  - 
four - 6 -  -

If you need the lower, you can modify the line:

var output = listmodel.Distinct(  ).ToList( );

To this:

var output = listmodel.OrderByDescending(x=> x.Prop2).Distinct(  ).ToList( );

The output change:

three - 7 -  - 
one - 6 -  - 
four - 6 -  - 
two - 1 -  -

Upvotes: 0

Mary
Mary

Reputation: 15091

From the MS docs for List "Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists." As you can see duplicate properties are not a problem. I made a little class. It could have been a structure. In the main program I created a List and added the objects. I hope this mets most of your requirements. When removing items from the list iterating through the list use a for loop backwards because indexes change when items are removed.

private void TestList()
        {
            List<SomeObject> lst = new List<SomeObject>();
            //Add to the list
            lst.Add(new SomeObject("Mary", "Jones", 50));
            lst.Add(new SomeObject("John", "Smith", 42));
            lst.Add(new SomeObject("James", "Peterson",50));
            lst.Add(new SomeObject("Mary", "Hanes", 62));
            //Sort the list
            var sortedByAge = from obj in lst orderby obj.Age select obj;
            Debug.Print("*****List Sorted by Age*****");
            foreach (SomeObject obj in sortedByAge)
                Debug.Print($"{obj.FirstName} {obj.LastName}'s age is {obj.Age}");
            var sortByLastName = from obj in lst orderby obj.LastName select obj;
            Debug.Print("*****List Sorted by LastName*****");
            foreach (SomeObject obj in sortByLastName)
                Debug.Print($"{obj.LastName}, {obj.FirstName}");
            //Delete from list
            for (int i = lst.Count - 1; i > -1; i--)
                if (lst[i].FirstName == "Mary")
                { lst.RemoveAt(i); }
            Debug.Print("*****List after Deletes*****");
            foreach (SomeObject item in lst)
                Debug.Print($"{item.FirstName} {item.LastName} {item.Age}");
        }
        public class SomeObject
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Age { get; set; }
            public SomeObject(string fname, string lname, int age)
            {
                FirstName = fname;
                LastName = lname;
                Age = age;
            }
        }

Upvotes: 2

Related Questions