开发者

How to sort the list with duplicate keys?

开发者 https://www.devze.com 2023-01-10 14:10 出处:网络
I have a set o开发者_高级运维f elements/keys which I\'m reading from two different config files. So the keys may be same but with different values associated with each of them.

I have a set o开发者_高级运维f elements/keys which I'm reading from two different config files. So the keys may be same but with different values associated with each of them.

I want to list them in the sorted order. What can I do ? I tried with SortedList class but it does not allow duplicate keys.

How can I do it?

e.g Lets say I have 3 elements with keys 1,2,3. Then i get one more element having key 2 (but different value). Then I want the new key to get inserted after existing key 2 but before 3. If I againg find an element with key 2, then it should go after most recently added key 2.

Please note than I'm using .NET 2.0


I prefer to use LINQ for this type of thing:

using System.Linq;

...

var mySortedList = myList.Orderby(l => l.Key)
                         .ThenBy(l => l.Value);

foreach (var sortedItem in mySortedList) {
    //You'd see each item in the order you specified in the loop here.
}

Note: you must be using .NET 3.5 or later to accomplish this.


what you need is a Sort function with a custom IComparer. What you have now is the default icomparer when you use sort. this will check on a field value.

When you create a custom IComparer (you do this in you class by implementing the Icomparable interface). what it does is: your object checks itself to every other object in the list you sort.

this is done by a function. (don't worry VS will implementd it when refering your interface

public class  ThisObjectCLass : IComparable{

    public int CompareTo(object obj) {
            ThisObjectCLass something = obj as ThisObjectCLass ;
            if (something!= null) 
                if(this.key.CompareTo(object.key) == 0){
                //then:
                   if .....
                }
                else if(this.value "is more important then(use some logic here)" something.value){
                 return 1
                }
                else return -1
            else
               throw new ArgumentException("I am a dumb little rabid, trying to compare different base classes");
        }
}

read on the links above for better information.

I know I had some troubles understanding this myself in the beginning, so for any extra help add a comment and I will elaborate


I did it by creating a SortedList<int, List<string>>. Whenever I find the duplicate key, I simply insert the value in the existing list associated with the key already present in the SortedList object. This way, I can have list of values for a particular key.


Use your own comparer class! If your keys in the sorted list are integers, you may use for example this comparer:

public class DegreeComparer : IComparer<int>
{
    #region IComparer<int> Members

    public int Compare(int x, int y)
    {
        if (x < y)
            return -1;
        else
            return 1;
    }

    #endregion
}

To instanciate a new SortedList with int keys and string values use:

var mySortedList = new SortedList<int, string>(new DegreeComparer());


If you don't really care about the sequence of the elements with equal keys, add everything to a list and then sort it by key:

static void Main(string[] args)
{
   List<KeyValuePair<int, MyClass>> sortedList = 
      new List<KeyValuePair<int, MyClass>>() {
         new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
         new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
         new KeyValuePair<int, MyClass>(5, new MyClass("five")),
         new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
         new KeyValuePair<int, MyClass>(7, new MyClass("seven-b"))
      };
   sortedList.Sort(Compare);
}
static int Compare(KeyValuePair<int, MyClass> a, KeyValuePair<int, MyClass> b)
{
   return a.Key.CompareTo(b.Key);
}

If you really want the items inserted later to be after those inserted earlier, sort them as they are inserted:

class Sorter : IComparer<KeyValuePair<int, MyClass>>
{

static void Main(string[] args)
{
   List<KeyValuePair<int, MyClass>> sortedList = new List<KeyValuePair<int, MyClass>>();
   Sorter sorter = new Sorter();
   foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] {
      new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
      new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
      new KeyValuePair<int, MyClass>(5, new MyClass("five")),
      new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
      new KeyValuePair<int, MyClass>(4, new MyClass("four-c")),
      new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) })
   {
      sorter.Insert(sortedList, kv);
   }
   for (int i = 0; i < sortedList.Count; i++)
   {
      Console.WriteLine(sortedList[i].ToString());
   }
}
void Insert(List<KeyValuePair<int, MyClass>> sortedList, KeyValuePair<int, MyClass> newItem)
{
   int newIndex = sortedList.BinarySearch(newItem, this);
   if (newIndex < 0)
      sortedList.Insert(~newIndex, newItem);
   else
   {
      while (newIndex < sortedList.Count && (sortedList[newIndex].Key == newItem.Key))
         newIndex++;
      sortedList.Insert(newIndex, newItem);
   }
}
#region IComparer<KeyValuePair<int,MyClass>> Members

public int Compare(KeyValuePair<int, MyClass> x, KeyValuePair<int, MyClass> y)
{
   return x.Key.CompareTo(y.Key);
}

#endregion
}

Or you could have a sorted list of lists:

static void Main(string[] args)
{
   SortedDictionary<int, List<MyClass>> sortedList = new SortedDictionary<int,List<MyClass>>();
   foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] {
      new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
      new KeyValuePair<int, MyClass>(7, new MyClass("seven")),
      new KeyValuePair<int, MyClass>(5, new MyClass("five")),
      new KeyValuePair<int, MyClass>(4, new MyClass("four-b")),
      new KeyValuePair<int, MyClass>(4, new MyClass("four-c")),
      new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) })
   {
      List<MyClass> bucket;
      if (!sortedList.TryGetValue(kv.Key, out bucket))
         sortedList[kv.Key] = bucket = new List<MyClass>();
      bucket.Add(kv.Value);
   }
   foreach(KeyValuePair<int, List<MyClass>> kv in sortedList)
   {
      for (int i = 0; i < kv.Value.Count; i++ )
         Console.WriteLine(kv.Value[i].ToString());
   }
}

I'm not sure if you can use List initializers in .NET 2.0 like I did in the first example above, but I'm sure you know how to populate a list with data.


.NET doesn't have huge support for stable sorts (meaning that equivalent elements maintain their relative order when sorted). However, you can write your own stable-sorted-insert using List.BinarySearch and a custom IComparer<T> (that returns -1 if the key is less than or equal to the target, and +1 if greater).

Note that List.Sort is not a stable sort, so you'd either have to write your own stable quicksort routine or just use insertion sort to initially populate the collection.


did you contemplate the NameValueCollection class as it allows you to store multiple values per key? you could for example have the following:

    NameValueCollection nvc = new NameValueCollection();
    nvc.Add("1", "one");
    nvc.Add("2", "two");
    nvc.Add("3", "three");

    nvc.Add("2", "another value for two");
    nvc.Add("1", "one bis");

and then to retrieve the values you could have:

    for (int i = 0; i < nvc.Count; i++)
    {
        if (nvc.GetValues(i).Length > 1)
        {
            for (int x = 0; x < nvc.GetValues(i).Length; x++)
            {
                Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i).GetValue(x));
            }
        }
        else
        {
            Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i)[0]);
        }

    }

which give the output:

'1' = 'one'

'1' = 'one bis'

'2' = 'two'

'2' = 'another value for two'

'3' = 'three'


In .NET 2.0 you can write :

List<KeyValuePair<string, string>> keyValueList = new List<KeyValuePair<string, string>>();

// Simulate your list of key/value pair which key could be duplicate
keyValueList.Add(new KeyValuePair<string,string>("1","One"));
keyValueList.Add(new KeyValuePair<string,string>("2","Two"));
keyValueList.Add(new KeyValuePair<string,string>("3","Three"));

// Here an entry with duplicate key and new value
keyValueList.Add(new KeyValuePair<string, string>("2", "NEW TWO")); 

// Your final sorted list with one unique key
SortedList<string, string> sortedList = new SortedList<string, string>();

foreach (KeyValuePair<string, string> s in keyValueList)
{
    // Use the Indexer instead of Add method
    sortedList[s.Key] = s.Value;
}

Output :

[1, One]
[2, NEW TWO]
[3, Three]


How about this

        SortedList<string, List<string>> sl = new SortedList<string, List<string>>();

        List<string> x = new List<string>();

        x.Add("5");
        x.Add("1");
        x.Add("5");
        // use this to load  
        foreach (string z in x)
        {
            if (!sl.TryGetValue(z, out x))
            {
                sl.Add(z, new List<string>());
            }

            sl[z].Add("F"+z);
        }
        // use this to print 
        foreach (string key in sl.Keys)
        {
            Console.Write("key=" + key + Environment.NewLine);

            foreach (string item in sl[key])
            {
                Console.WriteLine(item);
            }
        }


I had a similar issue where I was designing a game similar to the concept of a Chess game where you have the computer make a move. I needed to have the possibility of multiple pieces being able to make a move and thus I needed to have multiple Board-States. Each BoardState needed to be ranked based on the position of the pieces. For argument sake and simplicity, say my game was Noughts and Crosses and I was Noughts and the Computer was Crosses. If the board-state was showing 3 in a row of Noughts then this is the best state for me, if it shows 3 in a row of Crosses then this is the worst state for me and best for the computer. There are other states during the game that are more favourible to one or the other and furthermore there are muliplte states that result in a Draw, so how do I go about ranking it when there are equal rank scores. This is what I came up with (apologise in advance if you are not a VB programmer).

My comparer class:

Class ByRankScoreComparer
    Implements IComparer(Of BoardState)

    Public Function Compare(ByVal bs1 As BoardState, ByVal bs2 As BoardState) As Integer Implements IComparer(Of BoardState).Compare
        Dim result As Integer = bs2.RankScore.CompareTo(bs1.RankScore) 'DESCENDING order
        If result = 0 Then
            result = bs1.Index.CompareTo(bs2.Index)
        End If
        Return result
    End Function
End Class

My declarations:

Dim boardStates As SortedSet(Of BoardState)(New ByRankScoreComparer)

My Board-State implementation:

Class BoardState
    Private Shared BoardStateIndex As Integer = 0
    Public ReadOnly Index As Integer
    ...
    Public Sub New ()
        BoardStateIndex += 1
        Index = BoardStateIndex
    End Sub
    ...
End Class

As you can see RankScores are maintained in descending order and any 2 states having the same rank-score the later state goes to the bottom as it will always have a greater assigned Index and thus this allows duplicates. I can also safely call boardStates.Remove(myCurrentBoardState) which also uses the comparer and the comparer must return a 0 value in order to locate the objected to be deleted.

0

精彩评论

暂无评论...
验证码 换一张
取 消