why I get repeated object from List<E> in C#? - c#

this is my question function, I send the collection of data to the ShuffledList function in order to get one row at a time
private void nextQuestion_Question()
{
ql1 = ShuffleList(ql1);
currentQuestion_List = ql1.ElementAt(0);
//ql1.RemoveAt(0);
txtQ.Text = currentQuestion_List.text;
btnA.Text = currentQuestion_List.a;
btnB.Text = currentQuestion_List.b;
btnC.Text = currentQuestion_List.c;
btnD.Text = currentQuestion_List.d;
}
but this function some times it returns repeated objects
private List<E> ShuffleList<E>(List<E> inputList)
{
List<E> randomList = new List<E>();
Random r = new Random();
int randomIndex = 0;
while (inputList.Count>0)
{
randomIndex = r.Next(inputList.Count); //Choose a random object in the list
randomList.Add(inputList[randomIndex]); //add it to the new, random list
inputList.RemoveAt(randomIndex); //remove to avoid duplicates
}
return randomList; //return the new random list
}
note I'm try to make a multi choices game and it works well but some times repeat the same questions
please if you can help me to solve this issue

I think you problem in your repeatable shuffles. Try to store shuffled list once to prevent case when you generate the same question position twice during game session
bool listShuffled = false;
private void nextQuestion_Question()
{
if(!listShuffled){
listShuffled = true;
ql1 = ShuffleList(ql1);
}
currentQuestion_List = ql1.ElementAt(0);
//ql1.RemoveAt(0);
txtQ.Text = currentQuestion_List.text;
btnA.Text = currentQuestion_List.a;
btnB.Text = currentQuestion_List.b;
btnC.Text = currentQuestion_List.c;
btnD.Text = currentQuestion_List.d;
}

Related

SimpleSpriteSequence and getRandom

I have an Gameobject that contains several prefabs, As in the picture below
public SimpleSpriteSequence birds;
Randomizer setBirds;
setBirds = new Randomizer(birds.sprites);
int index = setBirds.getRandom();
birds.setCurrentSpriteIndex(index);
In the image below we see the population of the array
Invoke("Interface", 1f);
Now with the help of the random I get a random bird from the array. But the problem is that the bird can be repeated. All I want is that at least 6 times in a row when the Invoke("Interface", 1f); function does not repeat the bird.So in principle I have to do so 6 times the bird does not repeat itself. To be a random of these birds but 6 times to be different birds. No I know if I explained it properly, but I hope you understand the idea.
Here is code but it is written in console application since i do not have your code and it is writen with custom class of Birds but you will adapt it to your code. If you need help with it i will help you.
class Birds
{
public int birdID;
public string birdName;
//public Sprite birdSprite;
}
class Program
{
static Random rnd = new Random();
static Birds[] birds = new Birds[10];
static int nBirdsSpawned = 0;
static List<int> spawnedBirds = new List<int>();
static void Main(string[] args)
{
//Database of my birds ;)
birds[0] = new Birds();
birds[1] = new Birds();
birds[2] = new Birds();
birds[3] = new Birds();
birds[4] = new Birds();
birds[5] = new Birds();
birds[6] = new Birds();
birds[7] = new Birds();
birds[8] = new Birds();
birds[9] = new Birds();
birds[0].birdID = 0;
birds[0].birdName = "Bird 1";
birds[1].birdID = 1;
birds[1].birdName = "Bird 2";
birds[2].birdID = 2;
birds[2].birdName = "Bird 3";
birds[3].birdID = 3;
birds[3].birdName = "Bird 4";
birds[4].birdID = 4;
birds[4].birdName = "Bird 5";
birds[5].birdID = 5;
birds[5].birdName = "Bird 6";
birds[6].birdID = 6;
birds[6].birdName = "Bird 7";
birds[7].birdID = 7;
birds[7].birdName = "Bird 8";
birds[8].birdID = 8;
birds[8].birdName = "Bird 9";
birds[9].birdID = 9;
birds[9].birdName = "Bird 10";
int i = 0;
while (i < 100)
{
RandomSpawn();
i++;
}
Console.Write("Finished");
Console.ReadKey();
}
static void CheckForBirdSpawn(int birdID)
{
if (nBirdsSpawned <= 6)
{
if (!spawnedBirds.Contains(birdID))
{
spawnedBirds.Add(birdID);
SpawnBird(birdID);
}
else
{
Console.WriteLine("Bird " + birds[birdID].birdName + " is already spawned!");
}
}
else
{
SpawnBird(birdID);
}
}
static void SpawnBird(int birdID)
{
Console.WriteLine(birds[birdID].birdName);
nBirdsSpawned++;
}
static void RandomSpawn()
{
int r = rnd.Next(0, 10);
CheckForBirdSpawn(r);
}
}
Until first 6 birds are spawned it check if bird is already spawned and if it is it doesn't allow it's spawning. After 6 birds are spawned it allows every bird to be spawned.
Here is output in console app:
I know this is a mainly code reply but how about you try something a little like this?
Populate a list of the bird indexes.
Pick a random index within that list.
Decrease all already spawned birds' Values.
If any reach zero, re-add the birds index into the availableBird list.
Spawn the bird, while adding it's index as Key and the amount of spawning delay needed until it can be spawned again as the Value.
Remove the birds index from the list.
Do the spawn whenever necessary, this will spawn every frame since it is found in a Coroutine which has an infinite while loop.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class burd : MonoBehaviour {
public bool spawning;
private System.Random rnd = new System.Random();
private Dictionary<int, int> spawnedBurdies = new Dictionary<int, int>();
private List<int> availableBurdies = new List<int>();
// Use this for initialization
void Start () {
for (int index = 0; index < 10; index++)
{
availableBurdies.Add(index);
}
StartCoroutine(SpawnBurdies());
}
private IEnumerator SpawnBurdies()
{
while (true)
{
if (spawning)
{
int burdIndex = rnd.Next(availableBurdies.Count);
spawnBurd(availableBurdies[burdIndex]);
availableBurdies.RemoveAt(burdIndex);
}
yield return new WaitForFixedUpdate();
}
}
private void spawnBurd(int burdIndex)
{
Debug.Log("Spawned burd #" + burdIndex);
List<int> burdieKeys = new List<int>();
foreach (var burdie in spawnedBurdies) { burdieKeys.Add(burdie.Key); }
foreach (var burdieKey in burdieKeys)
{
spawnedBurdies[burdieKey]--;
if(spawnedBurdies[burdieKey] <= 0)
{
spawnedBurdies.Remove(burdieKey);
availableBurdies.Add(burdieKey);
}
}
spawnedBurdies.Add(burdIndex, 6);
}
}
This solution would save you from attempting to spawn birds that can't be spawned; but if there are less birds than the minimum spawns to wait for, an Argument out of range error would be thrown.

Initializing string array for data reference [duplicate]

This question already has answers here:
Meanings of declaring, instantiating, initializing and assigning an object
(2 answers)
Closed 6 years ago.
What I'm trying to do with this is save a list of reference keys for everything that is populating a listbox. There will be an unknown quantity of lines(users/"patients"). After saving the list, I want to be able to use the listbox index to find the corresponding key, and use that to move on to the next part.
public partial class PatientList : Window
{
HumanResources ListAllPatients;
List<PatientInformation> AllPatients;
string[] Usernames;
public PatientList()
{
InitializeComponent();
int i = 0;
string[] Usernames = new string[i];
ListAllPatients = new HumanResources();
AllPatients = ListAllPatients.PopPatientList();
foreach (PatientInformation PatientChoice in AllPatients)
{
Usernames[i] = PatientChoice.Username;
lboxPatients.Items.Add(string.Format("{0} {1}; {2}", PatientChoice.FirstName, PatientChoice.LastName, PatientChoice.Gender));
i += 1;
}
}
Here is the code for the button that moves you on to the next section
public void btnSelectPatient_Click(object sender, RoutedEventArgs e)
{
PatientInfo PatientInfoWindow = new PatientInfo();
PatientInfoWindow.Show();
//i = lboxPatients.SelectedIndex;
//UserFactory.CurrentUser = Usernames2[i];
this.Close();
}
My issue is this: I believe I've initialized a string array to do this, but VS keeps telling me it isn't assigned to, and will always remain null.
My question: How and where do I properly initialize a string array (or something better) in order to accomplish sending the key from PatientList() to btnSelectPatient?
You are not initializing the string[] field but a local variable in the constructor here:
string[] Usernames;
public PatientList()
{
InitializeComponent();
int i = 0;
string[] Usernames = new string[i];
You have to assign this array to the field:
public PatientList()
{
InitializeComponent();
int i = 0;
this.Usernames = new string[i];
But that doesn't make much sense currently because it's length is always 0.
Maybe you want this instead:
public PatientList()
{
InitializeComponent();
ListAllPatients = new HumanResources();
AllPatients = ListAllPatients.PopPatientList();
this.Usernames = new string[AllPatients.Count];
int i = 0;
foreach (PatientInformation PatientChoice in AllPatients)
{
Usernames[i] = PatientChoice.Username;
lboxPatients.Items.Add(string.Format("{0} {1}; {2}", PatientChoice.FirstName, PatientChoice.LastName, PatientChoice.Gender));
i += 1;
}
}
You have a few mistakes:
Your initializing a new local variable instead of the class-level variable because you declare it again (string[] userNames = ...
Your initializing an array of length 0. This will fail when you try to add items to position 1 and above.
My advice would be to use a List, as it's better suited for dynamic length lists and you don't need to keep track of the index yourself, it will automatically be added at the right index:
HumanResources ListAllPatients;
List<PatientInformation> AllPatients;
List<string> Usernames = new List<string>();
public PatientList()
{
InitializeComponent();
ListAllPatients = new HumanResources();
AllPatients = ListAllPatients.PopPatientList();
foreach (PatientInformation PatientChoice in AllPatients)
{
Usernames.Add(PatientChoice.Username);
lboxPatients.Items.Add(string.Format("{0} {1}; {2}", PatientChoice.FirstName, PatientChoice.LastName, PatientChoice.Gender));
}
}

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?
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);
}
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];
}
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.
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);
}
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;
}
}
}
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.

select randomly from string array without repetitions

Good day I have some problem regarding selecting a random string from my string array I am currently developing a guessing word game.
this is my string array:
string[] movie = {"deadpool", "batmanvssuperman", "findingdory", "titanic", "suicidesquad", "lordoftherings", "harrypotter", "jurassicpark", "hungergames", "despicableme" };
while this is the process in selecting a random string to my array, what should i do next, because I want to select the string not repeated.
e.g
when the program starts it will select a string then when i select random string again i want to not select the previous word that i've already selected previously.
string word = movie[r.Next(0, movie.Length)].ToUpper();
Thank you for response! Have a nice day.
Well, simply convert your array to list and shuffle it in random order :
var rand = new Random();
string[] movies = { "deadpool", "batmanvssuperman", "findingdory", "titanic", "suicidesquad", "lordoftherings", "harrypotter", "jurassicpark", "hungergames", "despicableme" };
List<string> randomMovies = movies.ToList();
for (int i = 0; i < movies.Length / 2; i++)
{
var randNum = rand.Next(i, randomMovies.Count);
var temp = randomMovies[randNum];
randomMovies[randNum] = randomMovies[i];
randomMovies[i] = temp;
}
Then you can just take random elements by :
var randomMovie = randomMovies.First();
randomMovies.Remove(randomMovie); // either remove it or use loop to iterate through the list
I sort of like to use Queue collection here :
var moviesQueue = new Queue<string>(randomMovies);
while (moviewQueue.Count > 0)
{
Console.WriteLine(moviewQueue.Dequeue());
}
P.S.
As suggested you don't really need to delete elements from randomMovie, you can save last used index in some field and use it later;
var lastIndex = 0;
var randomMovie = randomMovies[lastIndex++];
Just loop if it's been selected. This is untested code:
private string _last;
private string GetNonRepeatedMovie()
{
string selected = "";
do
{
selected = movie[r.Next(0, movie.Length)].ToUpper();
}
while (selected == this._last);
this._last = selected;
return selected;
}
This should work to select the initial string as well when the application starts.
If you need to keep a memory, convert your list to be a class that contains the name and a field of whether it has been chosen or not.
If you go through all of them, turn this semiphor off and begin again.
class GuessingName
{
public GuessingName(string name){Name = name;}
public string Name;
public bool chosen;
}
class RandomNamePicker{
private List<GuessingName> names;
public RandomNamePicker(){
names = new List<GuessingName>();
names.Add(new GuessingName("movie"));
}
string RandomPicker(){
if(names.All(c=>c.chosen))
names.ForEach(c=>c.chosen=false);
int r1 = r.Next(0, names.Length);
while(names[r1].chosen){
r1= r.Next(0,names.Length);
}
return names[r1].Name;
}
}

How to set array length in c# dynamically

I am still new to C# and I've been struggling with various issues on arrays. I've got an array of metadata objects (name value pairs) and I would like to know how to create only the number of "InputProperty" objects that I truly need. In this loop I've arbitrarily set the number of elements to 20 and I try to bail out when the entry becomes null but the web service on the receiving end of this does not like any null elements passed to it:
private Update BuildMetaData(MetaData[] nvPairs)
{
Update update = new Update();
InputProperty[] ip = new InputProperty[20]; // how to make this "dynamic"
int i;
for (i = 0; i < nvPairs.Length; i++)
{
if (nvPairs[i] == null) break;
ip[i] = new InputProperty();
ip[i].Name = "udf:" + nvPairs[i].Name;
ip[i].Val = nvPairs[i].Value;
}
update.Items = ip;
return update;
}
In summary, say I only have 3 namevalue pairs in the above input array? Rather than allocate 20 elements for the array called ip, how can code this so ip is only as big as it needs to be. The update object is passed across another webservice so serialization is important (i.e. I can't use namevaluecollection, etc.).
p.s. Is the only way to followup on a posted question through the "add comments" facility?
InputProperty[] ip = new InputProperty[nvPairs.Length];
Or, you can use a list like so:
List<InputProperty> list = new List<InputProperty>();
InputProperty ip = new (..);
list.Add(ip);
update.items = list.ToArray();
Another thing I'd like to point out, in C# you can delcare your int variable use in a for loop right inside the loop:
for(int i = 0; i<nvPairs.Length;i++
{
.
.
}
And just because I'm in the mood, here's a cleaner way to do this method IMO:
private Update BuildMetaData(MetaData[] nvPairs)
{
Update update = new Update();
var ip = new List<InputProperty>();
foreach(var nvPair in nvPairs)
{
if (nvPair == null) break;
var inputProp = new InputProperty
{
Name = "udf:" + nvPair.Name,
Val = nvPair.Value
};
ip.Add(inputProp);
}
update.Items = ip.ToArray();
return update;
}
If you don't want to use a List, ArrayList, or other dynamically-sized collection and then convert to an array (that's the method I'd recommend, by the way), then you'll have to allocate the array to its maximum possible size, keep track of how many items you put in it, and then create a new array with just those items in it:
private Update BuildMetaData(MetaData[] nvPairs)
{
Update update = new Update();
InputProperty[] ip = new InputProperty[20]; // how to make this "dynamic"
int i;
for (i = 0; i < nvPairs.Length; i++)
{
if (nvPairs[i] == null) break;
ip[i] = new InputProperty();
ip[i].Name = "udf:" + nvPairs[i].Name;
ip[i].Val = nvPairs[i].Value;
}
if (i < nvPairs.Length)
{
// Create new, smaller, array to hold the items we processed.
update.Items = new InputProperty[i];
Array.Copy(ip, update.Items, i);
}
else
{
update.Items = ip;
}
return update;
}
An alternate method would be to always assign update.Items = ip; and then resize if necessary:
update.Items = ip;
if (i < nvPairs.Length)
{
Array.Resize(update.Items, i);
}
It's less code, but will likely end up doing the same amount of work (i.e. creating a new array and copying the old items).
Use this:
Array.Resize(ref myArr, myArr.Length + 5);
Does is need to be an array? If you use an ArrayList or one of the other objects available in C#, you won't have this limitation to content with. Hashtable, IDictionnary, IList, etc.. all allow a dynamic number of elements.
You could use List inside the method and transform it to an array at the end. But i think if we talk about an max-value of 20, your code is faster.
private Update BuildMetaData(MetaData[] nvPairs)
{
Update update = new Update();
List<InputProperty> ip = new List<InputProperty>();
for (int i = 0; i < nvPairs.Length; i++)
{
if (nvPairs[i] == null) break;
ip[i] = new InputProperty();
ip[i].Name = "udf:" + nvPairs[i].Name;
ip[i].Val = nvPairs[i].Value;
}
update.Items = ip.ToArray();
return update;
}
Or in C# 3.0 using System.Linq you can skip the intermediate list:
private Update BuildMetaData(MetaData[] nvPairs)
{
Update update = new Update();
var ip = from nv in nvPairs
select new InputProperty()
{
Name = "udf:" + nv.Name,
Val = nv.Value
};
update.Items = ip.ToArray();
return update;
}
Use Array.CreateInstance to create an array dynamically.
private Update BuildMetaData(MetaData[] nvPairs)
{
Update update = new Update();
InputProperty[] ip = Array.CreateInstance(typeof(InputProperty), nvPairs.Count()) as InputProperty[];
int i;
for (i = 0; i < nvPairs.Length; i++)
{
if (nvPairs[i] == null) break;
ip[i] = new InputProperty();
ip[i].Name = "udf:" + nvPairs[i].Name;
ip[i].Val = nvPairs[i].Value;
}
update.Items = ip;
return update;
}
Typically, arrays require constants to initialize their size. You could sweep over nvPairs once to get the length, then "dynamically" create an array using a variable for length like this.
InputProperty[] ip = (InputProperty[])Array.CreateInstance(typeof(InputProperty), length);
I wouldn't recommend it, though. Just stick with the
List<InputProperty> ip = ...
...
update.Items = ip.ToArray();
solution. It's not that much less performant, and way better looking.
You can create an array dynamically in this way:
static void Main()
{
// Create a string array 2 elements in length:
int arrayLength = 2;
Array dynamicArray = Array.CreateInstance(typeof(int), arrayLength);
dynamicArray.SetValue(234, 0); // → a[0] = 234;
dynamicArray.SetValue(444, 1); // → a[1] = 444;
int number = (int)dynamicArray.GetValue(0); // → number = a[0];
int[] cSharpArray = (int[])dynamicArray;
int s2 = cSharpArray[0];
}

Categories

Resources