Answered Is object 'SortedSet' intended for primitive variables that are not arrays?

Elad

Member
Joined
Feb 15, 2020
Messages
20
Programming Experience
1-3
to example:
static SortedSet<int[]> stl = new SortedSet<int[]>();
If I try to add a second member as an array of int I get an error that I should implement an IComparable interface:
(A screenshot appears of error below image 1)
My question is is it at all correct to work with SortedSet with an array of int? Or at all with arrays of primitive types?
And so it's supposed to work, so how am I supposed to implement this interface with a variable of type? Rather, am I supposed to build a particular object?
And anyway, I found another way which is to work with HashSet which works in the same logic of SortedSet which prevents duplication but has a problem with it
When I have over 500,000 organs it does not really check if there is an added organ exists in the array,(A screenshot appears of below image 2)
Which means it keeps duplicates
How, after all, do I avoid duplications when the number of organs is very large?
1596052020755.png
1596052520569.png
 
In the future, please post your code in code tags rather than screenshots.

Also you can copy and paste the body of the exceptions by selecting "Copy exception detail to the clipboard"
 
If the type T you are passing in does not implement IComparable, then you can provide an IComparer<T>. For example:
C#:
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        var hashSet = new SortedSet<int[]>(Comparer<int[]>.Create(CompareIntArrays));

        int x = CompareIntArrays(null, new[] { 2 });

        hashSet.Add(new[] { 1, 2, 3 });
        hashSet.Add(new[] { 4, 5, 6 });

        int CompareIntArrays(int [] a, int [] b)
        {
            if (a == null && b == null)
                return 0;
            if (a != null && b == null)
                return 1;
            if (a == null && b != null)
                return -1;

            var comp = a.Length - b.Length;
            if (comp != 0)
                return comp;

            return a.Zip(b, (l, r) => l - r)
                    .TakeWhile(c => c == 0)
                    .FirstOrDefault();
        }
    }
}

Zip() will take a value from each collection and pass it into a function. In the case above my lambda computes the delta between the two values.
TakeWhile() above will skip over the leading value pairs that are equal.
FirstOrDefault() will return the first non-zero delta, or if all the value pairs are skipped, it will return the default integer value 0 which is coincidentally the value that indicates the two objects are equal.
 
I'd preference using Lists and Sorted Lists. But since you are not...

Here are your options, you can add IComparable like in this example :

Or re-declare your array values :
Clear your set and re-add the array
C#:
            sset.Add(new int[] { 1, 2, 3, 4 });
            sset.Clear();
            sset.Add(new int[] { 1,2,3,4,5,6,7,8 });
Or use a list to transition the data to append the array. Something like this example :
C#:
        public SortedSet<int[]> sset = new SortedSet<int[]>();

        private void Button1_Click(object sender, RoutedEventArgs e)
        {
            sset.Add(new int[] { 1, 2, 3, 4 });
            List<int> updatedLst = new List<int>();
            SortedSet<int[]>.Enumerator x = sset.GetEnumerator();
            while (x.MoveNext())
            {
                if (x.Current != null)
                {
                    x.Current.ToList().ForEach((int value) =>
                    {
                        updatedLst.Add(value);
                        Debug.WriteLine($"{value} was added to the new list");
                    });
                }
            }
            updatedLst.AddRange(new int[] { 5,6,7,8,9 });
            sset.Clear();
            sset.Add(updatedLst.ToArray());

            /* Now lets print your new values to the debug window */
            int iter = -1;
            foreach (int[] intarr in sset)
            {
                foreach (int i in intarr)
                {
                    iter++;
                    Debug.WriteLine($"The number at index {iter} is : {i}");
                }
            }
        }
 
Unfortunately, that is not what the OP is looking for. They are using an int [] to store a single lotto number guess. Multiple int [] are multiple guesses. They are trying to use the set to store multiple guesses. So the code above which just merges the new guesses with the existing entry won't let him/her later determine if they have a winning guess or not.
 
Sorry if I misunderstood.

It must have been that whooping screenshit that led me to believe he wanted to add more items to the array of int[] they already had stored in the set. It must have somehow drowned out the text and lured me into the assumption I made. lol My bad, its been a long day. . . .

What's wrong with going with a Hashset or Dictionary? A Hashset can determine if a particular item is in the set in live time, regardless of the number of items in it. This is probably the best option. Both can prevent duplicates too.
 
And he is using a HashSet<T> in his second screenshot as his solution to his problem. If only he had actually used code tags instead of screenshots, this would be so much easier to read and understand.
 
Unfortunately, the HashSet<T> uses the hash code of the reference, rather than the values. So the following code prints out "False":
C#:
var hashSet = new HashSet<int[]>();
hashSet.Add(new[] { 1, 2, 3 });
Console.WriteLine(hashSet.Contains(new[] { 1, 2, 3 }));

He's going to have to pass in an IComparer<T> into the constructor for the HashSet<T> like for the SortedSet<T> if he wants to compare values in the array rather than references to arrays.
 
Do something like this, but i highly advise adding message pumping here, I'm just too tired to implement it now :
C#:
        public HashSet<string> hs = new HashSet<string>();
        public StringBuilder strBuilder = new StringBuilder();
        public char[] numChars = "0123456789".ToCharArray();
        public readonly ThreadLocal<Random> randGenerator = new ThreadLocal<Random>(() => new Random());

And stick this in a method :
C#:
            for (int i = hs.Count; i < 20000000; i++)
            {
                int @int = randGenerator.Value.Next(0, 10);
                if (strBuilder.Length != 10)
                    strBuilder.Append(numChars[@int]);
                else
                {
                    hs.Add(strBuilder.ToString());
                    Debug.WriteLine($"New lotto number added : {strBuilder}");
                    strBuilder.Clear();
                }
            }
            Debug.WriteLine($"Hashset items has {hs.Count} items");
 
Works for me, and I got some 60000 entries and it handled well. Why you'd have an issue with over 500K is likely not down to the Hashset. The Hashset doesn't care how many items it has in it, as it knows if it already has an item, and therefore wont add one if it does.
 
Interesting approach... Break away from being strongly-typed to being stringly-typed instead. Simplifies matters. Sacrifice memory for simplicity -- something all software engineers have to balance out.

So instead of writing about 20 lines of new code -- new code that may have bugs -- to do array comparisons, use the built in string comparisons (that should have been debugged by Microsoft and has been road tested by millions of people) and use on average 6 bytes per lotto number column vs. the fixed 4 bytes per lotto number column of the int []. So with 6 lotto numbers, the string would on average use 38 bytes (4 byte length + 6 * 2 digits * 2 byte Unicode character + 5 * 2 Unicode character for space between numbers) vs the array using 28 bytes (4 byte length + 6 * 4 byte integer). 500K lotto guess comes out to be 19MB vs 15MB. What's an extra 4MB when you have several GB of RAM? Memory is cheap, right? Reliability is more expensive.
 
Here is the code for whoever asked:
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace LotoHashSet
{
    class Program
    {

        static HashSet<int[]> hashLoto = new HashSet<int[]>();
        const int SIZE = 1000000;
   

        static void Main(string[] args)
        {

            AddGusses();
        }
    
         

        static void AddGusses()
        {

            var rand = new Random();
            for (int i = 0; i < SIZE; i++)
            {
                var arrTemp = new int[7];
                for (int j = 0; j < arrTemp.Length - 1; )
                {
                    int num = rand.Next(1, 38);

                    if (!arrTemp.Contains(num))
                    {
                        arrTemp[j] = num;
                        j++;
                    }
                }

                Array.Sort(arrTemp);
                arrTemp[0] = rand.Next(1, 8);
                hashLoto.Add(arrTemp);
           
            }
        }
    }
}
 
Last edited by a moderator:
In the future, please put your code in code tags.
 
Back
Top Bottom