Ali97
Ali97

Reputation: 91

Eliminate duplicates from array c#

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?

Upvotes: 2

Views: 1851

Answers (6)

Robert M.
Robert M.

Reputation: 613

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.

Upvotes: 0

Damith
Damith

Reputation: 63065

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;
      }
    }
  }

Upvotes: 0

Miguel
Miguel

Reputation: 150

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);
        }

Upvotes: 0

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186688

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);
     } 

Upvotes: 5

Christian St.
Christian St.

Reputation: 1911

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.

Upvotes: 0

Patrick Hofman
Patrick Hofman

Reputation: 156978

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];
}

Upvotes: 2

Related Questions