2

I'm making a basic Deal or No Deal game, in doing so I have to pick 10 finalists from an array, at random, without repeats.

I have my structure and arrays set out like this

public struct People
{
    public string firstname;
    public string lastname;
    public int age;
}

class Program
{
    public static People[] People1 = new People[40];
    public static People[] Finalists1 = new People[10];
    public static People[] Finalist1 = new People[1];

And my finalists method set out like this

Random rand = new Random();

for (int i = 0; i < Finalists1.Length; i++)
{
    num = rand.Next(0, People1.Length);           
    Finalists1[i].lastname = People1[num].lastname;
    Finalists1[i].firstname = People1[num].firstname;
    Finalists1[i].age = People1[num].age;
}

How can I eliminate duplicate entries, while maintaining 10 people in the array?

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Ali97
  • 91
  • 8
  • 1
    Well, what do you define a duplicate? Identical firstname? LastName? Age? All three? Anyway there are dozenz of questions on StackOverflow, simply have a look for delete duplicates from collection or also select distinct values. – MakePeaceGreatAgain Nov 11 '16 at 09:04
  • You can create a list of numbers that have already been picked and make sure the new number isn't in the list of picked numbers. – tphx Nov 11 '16 at 09:05
  • 1
    Sort the array in *random order*, pick up `10` then – Dmitry Bychenko Nov 11 '16 at 09:05
  • I'm reasonably new to c#, but I've tried using a LINQ query and .Distinct, I tried using HashSets but I was receiving errors about converting type Assessment.People to int[] – Ali97 Nov 11 '16 at 09:06
  • 1
    First, you should not make `People` a struct but a class and second, you should call that class `Person`. Otherwise many people will assume your candidates are actually tribes. – Georg Nov 11 '16 at 09:09
  • What in particular have you tried? Write some code **to your question**. Furthermore be more specific on what kind of errors your got using these approaches. Currently your question is quite unclear. – MakePeaceGreatAgain Nov 11 '16 at 09:22

6 Answers6

5

Since initial array doesn't contain duplicates, you can sort it in random order and pick up 10 top items:

   Finalists1 = People1
     .OrderByDescending(item => 1)   // if people have some points, bonuses etc.
     .ThenBy(item => Guid.NewGuid()) // shuffle among peers
     .Take(10)                       // Take top 10
     .ToArray();                     // materialize as an array

If people are selected to the final are not completely random (e.g. contestant can earn points, bonuses etc.) change .OrderByDescending(item => 1), e.g.

     .OrderByDescending(item => item.Bonuses)

If you don't want to use Linq, you can just draw Peoples from urn without returning:

     private static Random random = new Random();  

     ... 

     List<People> urn = new List<People>(People1); 

     for (int i = 0; i < Finalists1.Length; ++i) {
       int index = random.Next(0, urn.Count);

       Finalists1[i] = urn[index];
       urn.RemoveAt(index);
     } 
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • When I go to write this out, it just takes the first ten, not in any shuffled order. Any ideas why? – Ali97 Nov 11 '16 at 09:22
  • 1
    @AlisterKyle: Nice catch! Wrong syntax: `new Guid` creates *zero* `Guid`, when `Guid.NewGuid() ` required *random* one. – Dmitry Bychenko Nov 11 '16 at 09:27
2

You can hold a list or hash set of numbers you have already drawn. Then just roll the dice again to get another random number.

Random rand = new Random();

HashSet<int> drawnNumbers = new HashSet<int>();
for (int i = 0; i < Finalists1.Length; i++)
{
    do
    {
        num = rand.Next(0, People1.Length);
    }
    while (drawnNumbers.Contains(num));

    Finalists1[i] = People1[num];
}
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • Since only 10 finalists are drawn, I think it would be better to replace `HashSet` by `List`. For maximum 10 items, a linear search is faster then a hash table loookup. – Georg Nov 11 '16 at 09:11
  • I know, but I think the idea is the same. Just wanted to use HashSet for OP to think about the impact of linear search on large data sets. – Patrick Hofman Nov 11 '16 at 09:13
0

You can change the type of Finalists1 to a HashSet, that does not allow duplicates. Then change your loop to

while(Finalists1.Length < 10)
{
    // random pick from array People1 (you don't need to create a new one)
    num = rand.Next(0, People1.Length);
    var toAdd = People1[num];
    // add to hash-set. Object won't be added, if already existing in the set
    Finalists1.Add(toAdd);
}

You probably need to override the Equals method of class People, if you really need to create a new object to add to the hash-set.

Christian St.
  • 1,751
  • 2
  • 22
  • 41
0

Swap each selected element in People1 to with the end of the array, and decrement an end-of-array index so that you're only selecting from what's left on the next iteration.

People tempPerson = new People;
int lastElem = People1.length - 1;
for (int i = 0; i < Finalists1.Length; i++)
{
    num = rand.Next(0, lastElem + 1);           
    Finalists1[i] = People1[num];

    //swap last entry in People1 with People1[num]
    tempPerson = People1[num];
    People1[num] = People1[lastElem];
    People1[lastElem] = tempPerson;

    lastElem--;
}

Sorry if there's a syntax error, I'm mostly using Java and C# these days.

BTW You don't have to set the fields individually since each array stores objects of type Person.

Robert M.
  • 575
  • 5
  • 17
0

You can group people array and select distinct that way. If you use List you can remove person from the list `var peopleArray = new People[40];

        var peopleCollection = peopleArray.GroupBy(p => new { p.age, p.firstname, p.lastname }).Select(grp => grp.FirstOrDefault()).ToList();

        var finalists = new People[10];


        var rand = new Random();

        for (var i = 0; i < finalists.Length; i++)
        {
            var index = rand.Next(0, peopleCollection.Count);
            var person = peopleCollection[index];

            finalists[i].lastname = person.lastname;
            finalists[i].firstname = person.firstname;
            finalists[i].age = person.age;

            peopleCollection.Remove(person);
        }
Miguel
  • 150
  • 7
0

shuffle and take the first 10, for example

People1.Shuffle();
Finalists1= People1.Take(10).ToArray();

you can find shuffle code from StackOverflow or search for "Fisher-Yates shuffle C#" Below methods are taken from This SO Post. Read the answers for more information on why GUID is not used etc..

public static class ThreadSafeRandom
  {
      [ThreadStatic] private static Random Local;

      public static Random ThisThreadsRandom
      {
          get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
      }
  }

  static class MyExtensions
  {
    public static void Shuffle<T>(this IList<T> list)
    {
      int n = list.Count;
      while (n > 1)
      {
        n--;
        int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
      }
    }
  }
Community
  • 1
  • 1
Damith
  • 62,401
  • 13
  • 102
  • 153