-2

I have a SortedList that adds KeyValuePairs every 10 min. I'm trying to keep the most recent 10 KeyValuePairs and remove all prior pairs but what I'm doing isn't working. Below I attached my code with explanation along each step. Any help is greatly appreciated.

private SortedList<int, double> myList = new SortedList<int, double>();

        // Every 10 minutes a new KeyValuePair is added to myList so 
        // I have no issue with sorting. I'm only trying to get the most 
        // recent 10 KeyValuePairs.

        // My Attempt (the only one that worked without errors)
        int mylistCount = 10;

        if (myList.Count()>mylistCount)
        {myList.Clear();}

        // The issue with my attempt is that it erases the entire myList
        // As a result, whenever myList reaches 10, it goes back to Zero.

        // What I'm trying to do is keep myList Count at 10 containing only
        // the most recent KeyValuePairs.

** In myList, the Key int is PlayerID# (which is random) and the Value is that Player's Score %

To answer your questions:

  • Sorting is not an issue with the current set up.
  • It does not have to be a SortedList, I'm open to any suggestion. I'm just more familiar with using Dictionaries and Lists.
  • I have never used a Queue but open to trying it. (Will have to research that, I'm learning something new everyday)
  • There are no time stamps and the timing of new entries is not important. All I'm trying to do is make sure that myList only has the most recent 10.
Koda
  • 37
  • 5
  • 4
    How do you know which ones are the most recent? It's sorting by `int`, so unless that `int` is reliably sequential I'm not sure how you'd identify the most recent items. – Joshua Robinson Jun 21 '19 at 20:23
  • 5
    Perhaps a [Queue](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1?view=netframework-4.8) would be more appropriate? – Broots Waymb Jun 21 '19 at 20:27
  • @BrootsWaymb: I thought the same, but if you take another read, he is using a `SortedList`. – JuanR Jun 21 '19 at 20:27
  • @JuanR - I noticed, but I don't see a reason he couldn't swap it out for a `Queue>`. – Broots Waymb Jun 21 '19 at 20:29
  • 2
    @JuanR, there is nothing in the question indicating that using a SortedList is a strong requirement and that using anything else than a SortedList is off the table. You choose the tool for the job, or the job for the tool, but you don't bring a hammer if you want to cut a sheet of paper. Unless Koda gives a reason for why they have to stick with SortedList, suggesting alternatives is fine, if you ask me. –  Jun 21 '19 at 20:30
  • @elgonzo: Switching to a queue would mean losing the sorted aspect and having to manage that manually. I am guessing he doesn't want to do that but it's up to the OP. – JuanR Jun 21 '19 at 20:33
  • 2
    @JuanR, yes i agree. But we don't really know much about Koda's use case and all, so basically making any suggestions right here and now is like throwing stuff at the wall and hope something will stick... :-) –  Jun 21 '19 at 20:35
  • 1
    @elgonzo: This is precisely why I didn't suggest a `Queue`. Because we don't know enough yet... :-) – JuanR Jun 21 '19 at 20:38
  • My thinking was the same as yours, @JuanR. What @Koda has is a list of `double` sorted by some arbitrary `int`. That suggested to me that the sorting was important, I confess that was just an assumption on my part. It isn't clear from the question. – Joshua Robinson Jun 21 '19 at 20:40
  • 2
    Perhaps a second collection like a fixed size array for the recent 10 items, acting like a ring buffer, could be used to keep track of the most recent 10 items. The whole shebang with the SortedList and the ring buffer could be encapsulated in a custom class to keep stuff neat and tidy... –  Jun 21 '19 at 20:43
  • 1
    @elgonzo: Agreed. Tough to tell unless the OP tells us how the key is calculated. For all we know, if could be sequential, in which case it would be simple. – JuanR Jun 21 '19 at 20:44
  • @JuanR, yeah, i was just having a bit of a good time throwing stuff at the wall there... ;-P –  Jun 21 '19 at 20:45
  • 2
    Clearly, as it stands this question is a little, well, *unclear*. It doesn't seem like a difficult problem at all, but we're missing some details that we need in order for us to help you @Koda. – Broots Waymb Jun 21 '19 at 20:47
  • 1
    Wow! I didn't expect to get this much feedback, Thank you all very much! – Koda Jun 22 '19 at 05:01
  • Enjoyed reading all stuff "thrown at the wall" here, Much appreciated. – Koda Jun 22 '19 at 05:24

5 Answers5

2

What I'm trying to do is keep myList Count at 10 containing only the most recent KeyValuePairs.

You're wanting to keep the 10 most recent pairs so I assume the sorting is by addition time. If that's true, you don't need to have them sorted and thus do not need a SortedList. You could use a Queue as suggested in a comment.

A queue is first in, first out (FIFO). That means you know the first element in the queue is the oldest and the one you need to dequeue when the eleventh element comes in. For example, couldn't this do the trick with little ceremony?

// q is a Queue (FIFO)
if (q.Count == 10)
{
    // we've reached our cap, remove the element at the 
    // front of the q (the oldest one)
    q.Dequeue();
}
// we'll always add the newest element to the end of the q
q.Enqueue(new KeyValuePair<int, double>(key, value));
ChiefTwoPencils
  • 13,548
  • 8
  • 49
  • 75
  • thank you very much for your response. Sorting is not by addition time, Sorting in myList is First In And It Stays In and that's the sequence I've been relying on to extract the information I need. The problem with that is that the SortedList eventually becomes too heavy to process without clearing myList. Do you have any recommendations for this? Also, Do you have any advice on how to convert my existing SortedList to a Queue? Thank you very much! – Koda Jun 22 '19 at 05:35
1

Without knowing much about the key, I offer a simple solution:

Create a class to represent the value as well as the time it was added and implements the IComparable<T> interface:

public class TimeStampedDouble  : IComparable<TimeStampedDouble>
{
    public TimeStampedDouble(double value)
    {
        Value = value;
        Date = DateTime.Now;
    }

    public double Value { get; private set; }
    public DateTime Date { get; private set; }

    public int CompareTo(TimeStampedDouble other)
    {
        return this.Date.CompareTo(other.Date);
    }

    // User-defined conversion to double, for convenience
    public static implicit operator double(TimeStampedDouble d)
    {
        return d.Value;
    } 

}

Change your list to store this type instead:

SortedList<int, TimeStampedDouble> list = new SortedList<int, TimeStampedDouble>();

Add items to the list using the new class:

//In this line, 1 is the key, 6 is the double you are storing.
myList.Add(1, new TimeStampedDouble(6));
myList.Add(3, new TimeStampedDouble(5));
myList.Add(2, new TimeStampedDouble(4));
myList.Add(7, new TimeStampedDouble(3));
myList.Add(5, new TimeStampedDouble(2));

You can now get the oldest item using Linq and remove it:

if (myList.Count() > mylistCount)
{
    var oldest = myList.OrderByDescending(i => i.Value).FirstOrDefault();        
    myList.Remove(oldest.Key);
}

Item with key 5 is removed.

It is not necessary to check if oldest is null because a) it's a value type and b) a check is made for a minimum number of items so the assumption is that the list will always have at least one item, provided mylistCount is greater than 0.

Because an implicit conversion to double is provided, you can use the value without explicit casting:

double doubleValue = myList[7];
JuanR
  • 7,405
  • 1
  • 19
  • 30
  • Maybe my brain is already on the weekend... but wouldn't that remove the newest item? Instead of the oldest? – Joshua Robinson Jun 21 '19 at 21:10
  • @JoshuaRobinson: Time for a break. It's sorting in descending order (`OrderByDescending`). :-) – JuanR Jun 21 '19 at 21:11
  • Thank you JuanR for your answer. I tried getting the oldest item using Linq and removing it but it only does it for the last only. How can I continue removing all up to the point where myList.Count==10? – Koda Jun 22 '19 at 05:17
1

How about using a LinkedList instead of a SortedList.

if(myLinkedList.Count() > 10)
    myLinkedList.RemoveFirst();

This will always remove the first added item of the list.

Mirko Brandt
  • 455
  • 3
  • 8
0

I think that the most convenient solution would be to use a bounded list, to ensure that the elements in the list will never exceed the maximum count. Implementing such a list is not very difficult. Probably the most flexible way is to implement the IDictionary<TKey, TValue> interface, delegating the work to an internal SortedList<TKey, TValue>. Bellow is an inheritance-based approach, that requires less code. Every time an added element causes the Count to become larger than the boundedCapacity, the oldest element in the list is automatically removed.

public class BoundedSortedList<TKey, TValue> : SortedList<TKey, TValue>
{
    private readonly int _boundedCapacity;
    private readonly List<TKey> _queue = new List<TKey>();

    public BoundedSortedList(int boundedCapacity)
    {
        _boundedCapacity = boundedCapacity;
    }

    public new void Add(TKey key, TValue value)
    {
        base.Add(key, value);
        _queue.Add(key);
        if (this.Count > _boundedCapacity)
        {
            var keyToRemove = _queue[0];
            this.Remove(keyToRemove);
        }
    }

    public new TValue this[TKey key]
    {
        get { return base[key]; }
        set { this.Remove(key); this.Add(key, value); }
    }

    public new bool Remove(TKey key) { _queue.Remove(key); return base.Remove(key); }
    public new bool RemoveAt(int index) => throw new NotImplementedException();
    public new void Clear() { base.Clear(); _queue.Clear(); }
}

Usage example:

var myList = new BoundedSortedList<int, double>(10);

Incorrect usage example:

var myIList = (IDictionary<int, double>)myList;

This will not work because accessing the class through the interface will bypass the logic that makes the list bounded.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
0

Here is what worked for me:

if (myList.Count()>mylistCount)
{myList.Remove(myList.FirstOrDefault());}

Thank you all

Koda
  • 37
  • 5