Question

How to access the item after in a SortedSet?

string entry;
private SortedSet<string> pastEntriesSS = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);

entry = entry.Replace("\t", "").TrimStart();
entry = Regex.Replace(entry, @"\s+", " ");
string s = pastEntriesSS.FirstOrDefault(x => x.StartsWith(entry.Trim(), StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(s))
{
    if(string.Compare(s,entry.Trim(),true) == 0)
    {
        // what I what here is the entry after s
        // how do I get the index of s
        int index = -1;
        s = pastEntriesSS.ElementAt(index + 1);
        if (!string.IsNullOrEmpty(s)) entry = s;
    }
    else 
        entry = s;
}

This is used for an autocomplete and if they hit that tab on an exact match then I want to get the next

I think I was looking at this wrong
This is what I came up with

entry = entry.Replace("\t", "").TrimStart();
entry = Regex.Replace(entry, @"\s+", " ");
string s = pastEntriesSS.FirstOrDefault(x => x.StartsWith(entry, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(s))
{
    if(string.Compare(s,entry,true) == 0)
    {   // it they tabbed on an  exact match then go to next or prior
        if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
        {
            s = pastEntriesSS.Reverse().FirstOrDefault(x => string.Compare(x, s, true) < 0);
        }
        else
        {
            s = pastEntriesSS.FirstOrDefault(x => string.Compare(x, s, true) > 0);
        }
        if (!string.IsNullOrEmpty(s)) entry = s;
    }
    else 
        entry = s;
}
Was it helpful?

Solution

Using the logic provided by the asker, here is a way to provide ElementBefore and ElementAfter access to any SortedSet using extension methods:

public static class BeforeAfterExtension
{
    //The true workhorse of the code!
    public static T ElementBefore<T>(this SortedSet<T> mySet, T val, IComparer<T> comparer)
    {
        //we are getting the first element el such that it is before
        //the provided value val. We must reverse the set because
        //otherwise, the first element in the set is always the first
        //to be less than the provided value.
        return mySet.Reverse().FirstOrDefault(el => comparer.Compare(el, val) < 0);
    }

    //Contains all the actual logic
    public static T ElementAfter<T>(this SortedSet<T> mySet, T val, IComparer<T> comparer)
    {
        //we are getting the first element el such that it is after
        //the provided value val.
        return mySet.FirstOrDefault(el => comparer.Compare(el, val) > 0);
    }

    //use the existing logic, but use the default comparer for the set
    public static T ElementBefore<T>(this SortedSet<T> mySet, T val)
    {
        return ElementBefore(mySet, val, mySet.Comparer);
    }

    //use the existing logic, but use the default comparer for the set
    public static T ElementAfter<T>(this SortedSet<T> mySet, T val)
    {
        return ElementAfter(mySet, val, mySet.Comparer);
    }

    //provide a condition that the element must already exist in the set
    //Consider a set of strings containing only "apple" and "cat"
    //without forcing the element to exist in the set, we could search for
    //the element before "bear" and get "apple" even though "bear" is not
    //in the set. Forcing the element to exist by setting mustExist=true
    //would return null if the element is not there already
    public static T ElementBefore<T>(this SortedSet<T> mySet, T val, bool mustExist, IComparer<T> comparer) {
    {
        if (mustExist)
        {
            if (mySet.Contains(val))
            {
                //take advantage of existing logic
                return ElementBefore(mySet, val, comparer);
            }
            else
            {
                return null;
            }
        }
        else
        {
            //take advantage of existing logic
            return ElementBefore(mySet, val, comparer);
        }
    }

    //provide a condition that the element must already exist in the set
    //Consider a set of strings containing only "apple" and "cat"
    //without forcing the element to exist in the set, we could search for
    //the element after "bear" and get "cat" even though "bear" is not
    //in the set. Forcing the element to exist by setting mustExist=true
    //would return null if the element is not there already
    public static T ElementBefore<T>(this SortedSet<T> mySet, T val, bool mustExist, IComparer<T> comparer) {
    {
        if (mustExist)
        {
            if (mySet.Contains(val))
            {
                //take advantage of existing logic
                return ElementAfter(mySet, val, comparer);
            }
            else
            {
                return null;
            }
        }
        else
        {
            //take advantage of existing logic
            return ElementAfter(mySet, val, comparer);
        }
    }

    //just use the default set comparer
    public static T ElementBefore<T>(this SortedSet<T> mySet, T val, bool mustExist)
    {
        //take advantage of existing logic
        return ElementBefore(mySet, val, mustExist, mySet.Comparer);
    }

    //just use the default set comparer
    public static T ElementAfter<T>(this SortedSet<T> mySet, T val, bool mustExist)
    {
        //take advantage of existing logic
        return ElementAfter(mySet, val, mustExist, mySet.Comparer);
    }
}

There are some additional methods in there that allow the customization of the comparer used, and to require the element exists in the set.

Now all that is required is

if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
    s = pastEntriesSS.ElementBefore(s);
}
else
{
    s = pastEntriesSS.ElementAfter(s);
}

and this is extensible to sets containing any kind of object, not just strings

OTHER TIPS

If I understand correctly, you are trying to get the index of the value s, then retrieve the element right after it.

You could accomplish this with an extension method that retrieves the index of s:

public static class MyExtensions
{
    public static int IndexOf<T>(this SortedSet<T> mySet, T val)
    {
        int index = 0;
        foreach (T el in mySet) {
            if (mySet.Comparer.Compare(el, val) == 0) {
                return index;
            }
            index++;
        }
        return -1;
    }

    public static int IndexOf<T>(this SortedSet<T> mySet, T val, IEqualityComparer<T> comparer)
    {
        int index = 0;
        foreach (T el in mySet) {
            if (comparer.Equals(val, el)) {
                return index;
            }
            index++;
        }
        return -1;
    }
}

Which let's you call IndexOf on your set:

int index = pastEntriesSS.IndexOf(s);
if (index >= 0)
{
    s = pastEntriesSS.ElementAt(index + 1);
    if (!string.IsNullOrEmpty(s)) entry = s;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top