select randomly from string array without repetitions - c#

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

Related

why I get repeated object from List<E> in 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;
}

List sorting by multiple parameters

I have a .csv with the following headers and an example line from the file.
AgentID,Profile,Avatar,In_Time,Out_Time,In_Location,Out_Location,Target_Speed(m/s),Distance_Traveled(m),Congested_Duration(s),Total_Duration(s),LOS_A_Duration(s),LOS_B_Duration(s),LOS_C_Duration(s),LOS_D_Duration(s),LOS_E_Duration(s),LOS_F_Duration(s)
2177,DefaultProfile,DarkGreen_LowPoly,08:00:00,08:00:53,East12SubwayportalActor,EWConcourseportalActor,1.39653,60.2243,5.4,52.8,26.4,23,3.4,0,0,0
I need to sort this .csv by the 4th column (In_time) by increasing time ( 08:00:00, 08:00:01) and the 6th (In_Location) by alphabetical direction (e.g. East, North, etc).
So far my code looks like this:
List<string> list = new List<string>();
using (StreamReader reader = new StreamReader("JourneyTimes.csv"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
line.Split(',');
list.Add(line);
}
I read in the .csv and split it using a comma (there are no other commas so this is not a concern). I then add each line to a list. My issue is how do I sort the list on two parameters and by the headers of the .csv.
I have been looking all day at this, I am relatively new to programming, this is my first program so I apologize for my lack of knowledge.
You can use LINQ OrderBy/ThenBy:
e.g.
listOfObjects.OrderBy (c => c.LastName).ThenBy (c => c.FirstName)
But first off, you should map your CSV line to some object.
To map CSV line to object you can predefine some type or create it dynamically
from line in File.ReadLines(fileName).Skip(1) //header
let columns = line.Split(',') //really basic CSV parsing, consider removing empty entries and supporting quotes
select new
{
AgentID = columns[0],
Profile = int.Parse(columns[1]),
Avatar = float.Parse(columns[2])
//other properties
}
And be aware that like many other LINQ methods, these two use deferred execution
You are dealing with two distinct problems.
First, ordering two columns in C# can be achieved with OrderBy, ThenBy
public class SpreadsheetExample
{
public DateTime InTime { get; set; }
public string InLocation { get; set; }
public SpreadsheetExample(DateTime inTime, string inLocation)
{
InTime = inTime;
InLocation = inLocation;
}
public static List<SpreadsheetExample> LoadMockData()
{
int maxMock = 10;
Random random = new Random();
var result = new List<SpreadsheetExample>();
for (int mockCount = 0; mockCount < maxMock; mockCount++)
{
var genNumber = random.Next(1, maxMock);
var genDate = DateTime.Now.AddDays(genNumber);
result.Add(new SpreadsheetExample(genDate, "Location" + mockCount));
}
return result;
}
}
internal class Class1
{
private static void Main()
{
var mockData = SpreadsheetExample.LoadMockData();
var orderedResult = mockData.OrderBy(m => m.InTime).ThenBy(m => m.InLocation);//Order, ThenBy can be used to perform ordering of two columns
foreach (var item in orderedResult)
{
Console.WriteLine("{0} : {1}", item.InTime, item.InLocation);
}
}
}
Now you can tackle the second issue of moving data into a class from Excel. VSTO is what you are looking for. There are lots of examples online. Follow the example I posted above. Replace your custom class in place of SpreadSheetExample.
You may use a DataTable:
var lines = File.ReadAllLines("test.csv");
DataTable dt = new DataTable();
var columNames = lines[0].Split(new char[] { ',' });
for (int i = 0; i < columNames.Length; i++)
{
dt.Columns.Add(columNames[i]);
}
for (int i = 1; i < lines.Length; i++)
{
dt.Rows.Add(lines[i].Split(new char[] { ',' }));
}
var rows = dt.Rows.Cast<DataRow>();
var result = rows.OrderBy(i => i["In_time"])
.ThenBy(i => i["In_Location"]);
// sum
var sum = rows.Sum(i => Int32.Parse(i["AgentID"].ToString()));

Sending a list of doubles as strings to the database

Just quite confused with the piece of code below.
I have class like below
public class Counter
{
public Double NormalCounter { get; set; }
public Double SATCounter { get; set; }
public Double SUNCounter { get; set; }
}
in my main class i have method to do some calculation to fill the counter
Counter CountHrs = GetBookedHours(resourceBkg, PrevEvent);
var lstExpResult = new List<string> {CountHrs.NormalCounter.ToString(),
CountHrs.SATCounter.ToString(),
CountHrs.SUNCounter.ToString()};
UpdateBooking(bookingSesid, lstExpResult);
Just assume i have the value like below in the counter
NormalCounter =4
SATCounter=10
SUNCounter=6
am trying to add in to string list and update the database.is that the right way to do ? or any other options i have please.
my update booking method is below to give clear idea.
public static bool UpdateBooking(string BookingSesid,List<string>HoursByRate)
{
SchedwinEntities db = new SchedwinEntities();
string value = string.Empty;
for (int i = 0; i < 5; i++)
{
string str = " ";
if (i < HoursByRate.Count())
{
str = HoursByRate[i];
value += str + ((char)13).ToString() + ((char)10).ToString();
}
}
var query =
from SEVTs in db.SEVTs
where
SEVTs.SESID.Trim() == BookingSesid//SESID
select SEVTs;
foreach (var SEVTs in query)
{
SEVTs.USER3 = value;//value
}
try
{
db.SaveChanges();
return true;
}
catch (UpdateException ex)
{
return false;
}
}
Rather than passing a list of strings that represent doubles, you should pass a list of key-value pairs, construct a parametersized statement from them, and use the list of key-value-pairs to bind parameter values, like this:
class SqlParamBinding {
string Name {get;set;}
object Value {get;set;}
}
var lstExpResult = new List<SqlParamBinding> {
new SqlParamBinding { Name = "NormalCounter", Value = CountHrs.NormalCounter}
, new SqlParamBinding { Name = "SATCounter", Value = CountHrs.SATCounter}
, new SqlParamBinding { Name = "SUNCounter", Value = CountHrs.SUNCounter}
};
UpdateBooking(bookingSesid, lstExpResult);
Now that lstExpResult separates names from values, your UpdateBooking code could format the SQL expression as
WHERE NormalCounter=#NormalCounter AND SATCounter=#SATCounter AND ...
and then bind #NormalCounter, #SATCounter, and #SUNCounter to the values passed in the lstExpResult list.
If it is going to list of Counter classes then have List instead of List. Looks like you might be having bunch of Counter objects that might be getting updated or sent to the database.
Counter c = new Counter();
c. NormalCounter = 4
c.SATCounter = 10
c.SunCounter = 10
List<Counter> listCounter = new List<Counter>
listCounter.Add(c);
Code is more maintainable and readable.
If you are sending one object at a time, then no need of list at all. You can pass in the counter object to your UpdateMethod and parse it while updating the database.

increasing names in C# with integers

I need to get an object and check if it already exists.
In case it does, I want to add a number, or increase the number in its name.
For example if I have the object "a" and it exists, I need to add a 1 so it's called a1.
In case a1 exists, a2, etc.
How could I perform this?
My code:
if (e.TreeNode.Tag is Variant)
{
if (variantExists(e.TreeNode.Text))
{
Random r = new Random();
int randomNumber = r.Next(0, 99);
e.TreeNode.Text = e.TreeNode.Text + randomNumber;
//e.TreeNode.Remove();
return;
}
}
Can you change the TreeNode class? I would add properties for Label (Name without Index) and Index and make the Name property read only, i.e
class TreeNode
{
public int Index {get;set;}
public string Label {get;set;}
public string Name
{
get { return Index == 0 ? Label : Label + Index; }
}
}
In your code you just need to set the Index property to the value you need and dont worry about the whole string parsing stuff
string name = "SomeName";
string tempName = name;
int n = 0;
while (DoesNameExist(tempName))
{
n++;
tempName = name + n;
}
name = tempName;
This gets ineffecient for large numbers of the same object, but that shouldn't happen right?
The problem with doing it the other way around, and stripping off trailing numbers to find the "original" name is that the original name may genuinely have numbers on it.
Eg. You say you add:
SomeName
SomeName99
SomeName
SomeName99
The above code will give you
SomeName
SomeName1
SomeName99
SomeName991
Something along the lines of this could work:
var existingItems = new HashSet<string>();
var items = new List<string>{"a", "b", "a"};
foreach (var item in items)
{
var tempItem = item;
var i = 1;
while (!existingItems.Add(tempItem))
tempItem = tempItem + i++;
}

binding list variable one after another

this is my class
public class Users
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public string Country { get; set; }
}
i am defing an list variable
List<Users> myList = new List<Users>
i have four functions each one returing a string array
{of data content like, names, age, gender, country}
**functions names**
FunNames();
FunAge();
Fungender();
Funcountry();
now i need to bind these these return values of all these functions into list one by one
like.
myList =FunNames();
myList =FunAge();
myList Fungender();
.....
hope my Question is clear.
any help would be great thank you.
There is no easy way to do what you require as far as I know. You have to iterate all of those arrays, generate the Users instances and add them to your list.
string[] names = FunNames();
string[] ages = FunAge();
string[] genders = Fungender();
string[] countries = FunCountry();
/* have to make sure they are not of different lengths */
int minLength = names.Length;
if (ages.Length < minLength)
minLength = ages.Length;
if (genders.Length < minLength)
minLength = genders.Length;
if (countries.Length < minLength)
minLength = countries.Length;
for(int i=0 ; i < minLength; i++)
{
Users item = new Users();
item.Name = names[i];
item.Age = int.Parse(ages[i]);
item.Gender = genders[i];
item.Country = countries[i];
myList.Add(item);
}
If I understand your problem correctly, FunNames() et al each return a portion of the Users object. This can make your problem a little bit difficult because the data for each part of the Users object is stored in a separate array. Can we assume that each array is of the same length and that the corresponding positions in each array combine to make the various properties in the Users object? If so, we can use code like this:
public List<Users> GetUsersList()
{
List<Users> myList = new List<Users>();
string[] names = FunNames();
string[] ages = FunAge();
string[] genders = Fungender();
string[] countries = Funcountry();
for (int i = 0; i < names.Length; i++)
{
Users user = new Users();
user.Name = names[i];
user.Age = names[i];
user.Gender = names[i];
user.Country = names[i];
myList.Add(user);
}
return myList();
}
The above code will iterate through each of arrays of strings returned by your functions and subsequently populate Users object and add them to the list. Again, this assumes that your arrays "line up" and have the same length.
NOTE: You may consider changing the name of Users to User. Think about it: a User object tells the programmer to expect a single User, not multiple Users. Your myList object represents multiple individual Users.

Categories

Resources