random unique list - not providing unique values in C#

66 views Asked by At

I have created a form in visual studio C# and am trying to assign a unique random category to each label (numbered from 1 to 9), but it seems that there are repeat values every time that I click the randomize button. I took the code from the Fisher-Yates shuffle, which should work as intended, what part am I missing that gives my labels non-unique values? See code below:

   public void button2_Click(object sender, EventArgs e)

    {

        List<String> unshuffledCategories = new List<String> { "History", "Geography", "Music",    "Animals", "Logos", "Flags", "Food", "games", "Sports" };
        List<String> shuffledCategories = new List<string>();

        Random r = new Random();

        var labels = new List<Label> { lblField1, lblField2, lblField3, lblField4, lblField5, lblField6, lblField7, lblField8, lblField9 };

        for (int n = unshuffledCategories.Count; n > 0; --n)
        {
            int k = r.Next(n);

            String temp = unshuffledCategories[k];
            shuffledCategories.Add(temp);
        }

        for (int i = 0; i < labels.Count; i++)
        {
            labels[i].Text = shuffledCategories[i];
        }
    }

Hope someone can help me out, much appreciated <3!

I tried to create a form which pulls unique random elements from a string list I have created in C#, I was expecting it to provide random unique values, but values are nearly always repeated when I click the button to execute the code.

2

There are 2 answers

1
Jayson On

your problem is in this for loop. In each iteration of the loop you are getting a random number between 1 and (9-n). there is nothing preventing you from getting the same number on a subsequent iteration. In theory you could end up getting "1" all 9 times.

    for (int n = unshuffledCategories.Count; n > 0; --n)
    {
        int k = r.Next(n);

        String temp = unshuffledCategories[k];
        shuffledCategories.Add(temp);
    }

You could try this instead:

    for (int n = labels.Count; n > 0; --n)
    {
        int k = r.Next(unshuffledCategories.Count);

        String temp = unshuffledCategories[k];
        unshuffledCatogories.Remove(temp);
        shuffledCategories.Add(temp);
    }

This way you are removing the item from unshuffledCategories each time and getting a random value between 0 and the remaining elements so you wont end up with duplicates.

1
Dmitry Bychenko On

So, you want to shuffle a list, let's extract it as a method:

private static void Shuffle<T>(IList<T> list) {
  for (int i = 0; i < list.Count - 1; ++i) {
    int j = Random.Shared.Next(i, list.Count);

    (list[i], list[j]) = (list[j], list[i]);
  }
}

Then we can put the rest as easy as

var categories = new List<String> { 
  "History", "Geography", "Music", "Animals", "Logos", "Flags", "Food", "games", "Sports" 
};

Shuffle(categories);

var labels = new List<Label> { 
  lblField1, lblField2, lblField3, lblField4, lblField5, lblField6, lblField7, lblField8, lblField9 
};

for (int i = 0; i < labels.Count; i++) 
  labels[i].Text = categories[i];

Another approach is to use Linq:

using System.Linq;

...

var categories = new List<String> { 
  "History", "Geography", "Music", "Animals", "Logos", "Flags", "Food", "games", "Sports" 
};

var labels = new List<Label> { 
  lblField1, lblField2, lblField3, lblField4, lblField5, lblField6, lblField7, lblField8, lblField9 
};

var pairs = categories
  .OrderBy(_ => Random.Shared.NextDouble()) // Shuffle
  .Zip(labels, (cat, lbl) => (cat, lbl));

foreach (var (cat, lbl) in pairs)
  lbl.Text = cat;