Control over random value - c#

I use a random for browse list I do like this:
var oServer = new SmtpServer("");
var random = new Random();
var randomIndex = random.Next(list.Count());
var socks = list[randomIndex];
oServer.SocksProxyServer = socks.IpAddress.ToString();
oServer.SocksProxyPort = socks.Port;
I want that if value selected, will be not selected again in next. I want that it choose another value.
What I should do?

If you can't remove from list itself, remove from possibleIndexes:
private static random = new Random();
...
List<int> possibleIndexes = Enumerable
.Range(0, list.Count())
.ToList();
...
int idx = random.Next(possibleIndexes.Count());
int index = possibleIndexes[idx];
possibleIndexes.RemoveAt(idx);
var socks = list[index];
...

Just remove the item from the list if you don't want to use it again:
var socks = list[randomIndex];
list.RemoveAt(randomIndex);

If you don't want to lose data in orginal list first copy the list to another one then remove item from your copy list
var copyList=new List<yourListType>();
copyList.AddRange(list);//copy form your orginal list
var socks = copyList[randomIndex];
copyList.RemoveAt(randomIndex);
Or you can generate unique random number
List<int> randomNumbers = new List<int>(list.Count());
do
{
randomIndex = random.Next();
} while (randomNumbers.Contains(randomIndex));
randomNumbers.Add(randomIndex);
var socks = list[randomIndex];

Related

Random method return two values without duplicate

I created two methods that take random values from database table
This method will return an integer random value
private int GetDayValue()
{
Random rnd = new Random();
List<int> days = GetDays();
int r = rnd.Next(days.Count);
return days[r];
}
This method will return also an integer random value
private int GetPeriodValue(string grd_id)
{
Random rnd = new Random();
List<int> periods = GetPeriods(grd_id);
int r = rnd.Next(periods.Count);
return periods[r];
}
Here there is a for loop will take random value from two methods
for (int z = 0; z <= secPortion; z++)
{
//here the random value of day
day = GetDayValue();
//here the random value of period
period = GetPeriodValue(grd_id);
//this method will insert the value on database table
SetFirstShot(teacher, sub, sec, period, day);
}
My question:
I want to create method will return random value of day and period together for example random value (ex: 3,5 ... 4,6 .... 1,1) and not repeated during the loop for example 4,6 will generated once time then another random value but if it 4,6 the method will create again a random value.
You can use a hash function instead that has a good random distribution aspect to it. A hash function with return the same value for the same inputs each time, while the results will "seem" random, which might be all the "randomness" you need.
using System;
using System.IO;
using System.Security.Cryptography;
// ...
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
using (var hash = MD5.Create())
{
writer.Write(day);
writer.Write(period);
var data = md5Hash.ComputeHash(stream.ToArray());
var randomNumber = BitConverter.ToInt32(data);
}
The added benefit is that you don't have to store previously-computed values in any sort of database. Any two agents with the same hashing algorithm will generate the same hashes. The values may not be completely unique, but they should vary wildly from input to input while also being deterministic with no hidden state. Truly unique would require querying outside state and probably introduce race conditions if your system is distributed.
It appears that GetDays() and GetPersiodValue() returns two int lists, so I'm writing two mock methods for those.
static List<int> GetDays()
{
return new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
}
static List<int> GetPeriods()
{
return new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
}
First thing you need to do is move the new Random() out of the methods (so that it's outside the loop). Otherwise when you iterate so fast it will keep giving you the same number.
static int GetRandomDay(Random rnd)
{
var days = GetDays();
return days[rnd.Next(days.Count)];
}
static int GetRandomPeriod(Random rnd)
{
var periods = GetPeriods();
return periods[rnd.Next(periods.Count)];
}
Then I'll do something like this to see if the generated pair of random values were generated before. Add to a list (or DB in your case) if not.
static void Main(string[] args)
{
var RandomList = new List<Tuple<int, int>>();
Random rnd = new Random();
for (int i =0; i < 10; i++)
{
bool isUnique = false;
do
{
int day = GetRandomDay(rnd);
int period = GetRandomPeriod(rnd);
if(RandomList.Where(x => x.Item1 == day && x.Item2 == period).FirstOrDefault() == null)
{
RandomList.Add(new Tuple<int, int>(day, period));
isUnique = true;
}
} while (!isUnique);
}
Console.ReadLine();
}
I tried to use KeyValuePair and I solved, its works well !
Here the code that I used:
// I created a list contain two pairs..
List<KeyValuePair<int, int>> dictionary = new List<KeyValuePair<int, int>>();
for (int z = 0; z < secPortion; z++)
{
day = GetDayValue();
period = GetPeriodValue(grd_id);
//here I pass the value of day and period to the pairs
KeyValuePair<int, int> myItem = new KeyValuePair<int, int>(day, period);
//here I created a list which is the list of values for the random key, if it is exist or not.
List<int> values = (from kvp in dictionary where kvp.Key == day select kvp.Value).ToList();
// this is a bool variable its value by default is true, once there is a duplicated value it will be changed to false....
bool FirstChecker = true;
if (values.Contains(period))
{
FirstChecker = false;
z--;
}
else
{
dictionary.Add(myItem);
SetFirstShot(teacher, sub, sec, period, day);
}
}
I think this is what you need.
Although the example is a solution for a one-dimensional array, actually in your case the two list days and periods can be mapped to one sequence whose length is days.Count * periods.Count. Or you can do this for each list separately but some cases will be missed.

Get listitems according to the index of another list

I have a List<List<string>> with three nested lists. Now I need to check if List[1] equals a certain string and if so, check if the value at this index in List[2] has another certain string. If both conditions return true, then I need to get that certain index and get the item of List[0].
For example:
var list = Titles[0];
var list2 = Titles[1];
var list3 = Titles[2];
foreach (var item in list2)
{
if (item.Contains("Dt."))
{
int idx = list2.IndexOf(item);
var value = list3.ElementAt(idx);
if (value.Contains("25.04.2017"))
{
var newList = list.ElementAt(idx);
}
}
}
This approach doesn't seem very efficient in regards to performance, especially if the nested list contains ~9000 items.
I tried to get the result via lambda expressions first, but I'm not sure if this is the right approach either.
What would be the best or most efficient solution?
Eliminate ElementAt with direct access to index. I believe ElementAt iterates over List in order to get i'th element
Eliminate usage of IndexOf with index provided by for loop I believe IndexOf iterates over List in order to find matching element.
var list = Titles[0];
var list2 = Titles[1];
var list3 = Titles[2];
for (int i = 0 ; i < list2.Count; ++ i)
{
var item = list2[i];
if (item.Contains("Dt."))
{
var value = list3[i];
if (value.Contains("25.04.2017"))
{
var newList = list[i];
}
}
}
Note if size of list2 is greater than size of list or list3 then you potentially get IndexOutOfRangeException
Lambda equivalent for your code:
if(list2.Any(item => item.Contains("Dt.")))
{
int idx = list2.IndexOf("Dt.");
if(list3.ElementAt(idx).Contains("25.04.2017"))
{
var newList = list.ElementAt(idx);
}
}
for (int i = 0; i < list2.Count; ++i)
{
var item = list2[i];
if (item.Contains("Dt."))
{
var value = list3[i];
if (value.Contains("25.04.2017"))
{
var newList = list[i];
break; // Break the loop :-)
}
}
}

Pass by reference issue using a delegate inside a loop

I have the following code which creates and returns a Dictionary<int, List<string>>
private static readonly Random rnd = new Random();
private static Dictionary<int, List<string>> GenerateValues(string entityType, int rows)
{
var values = new Dictionary<int, List<string>>();
var baseValues = new List<string> { "99 Hellesdon Road","Home","Norfolk","NR6 5EG","Norwich" };
Action<int> rowLoop = null;
switch (entityType)
{
case "Employer":
rowLoop = (row) =>
{
var employer = new List<string>
{
$"Employer {rnd.Next(10000, 99999)}",
$"{rnd.Next(100000000, 999999999)}",
"TRUE"
};
var rowValue = baseValues;
rowValue.AddRange(employer);
values.Add(row, rowValue);
};
break;
case "AssessmentCentre":
rowLoop = (row) =>
{
var rowValue = baseValues;
rowValue.Add($"Assessment Centre {rnd.Next(10000, 99999)}");
values.Add(row, rowValue);
};
break;
}
for (int i = 1; i <= rows; i++)
{
rowLoop(i);
}
return values;
}
And then to call it with...
var spreadsheetValues = GenerateValues("Employer", 5);
Then intention for the code is to have some base values stored in a List<string>, then depending on what entityType is passed into the method, add some additional values to those base values. Then loop round a certain number of rows to create the Dictionary<int, List<string>>, where int is the row number.
The issue I'm having is that baseValues is being updated during each iteration of my for loop. I didn't expect this when using var rowValue = baseValues;
This is a debug screenshot of the last item in the collection....
I am expecting each List<string> in Dictionary<int, List<string>> to contain 8 items, but instead it is growing with each iteration.
Why is this occurring and how might I correct it?
It seems like you are already on track with the solution to your problem but here is a way to solve it.
Where you write
var rowValue = baseValues;
you are really just making "rowValue" a pointer to "baseValues". To make a copy, you could do something like this:
var rowValue = baseValues.ToList();

How to populate a RadioButton List with 4 answer choices in random order for a Quiz?

I am trying to create a quiz widget based off of SharePoint list data (Questions and 4 answer choices).
I am able to find a way to pull 10 random rows and read the questions and answers.
However, what I cant seem to come up with is logic to populate the RadioButton list with the answer choices (for that specific row item) in a random order.
Here is the code I have thus far: Please suggest possible strategies/code
//Function to pull 10 random questions from the "QuestionsAndAnswers" list for the correspoding LessonPlan
public void LoadQuestions()
{
try
{
SPWeb thisWeb = SPContext.Current.Web;
//Accessing the list correspoding to the current LessonPLan subsute
SPList oSPList = thisWeb.Lists["QuestionsAndAnswers"];
//Using a query in case there is a need tofilter by LessonID
SPQuery oSPQuery = new SPQuery();
oSPQuery.RowLimit = 10;
SPListItemCollection oSPListItemCollection = oSPList.GetItems(oSPQuery);
Random rand = new Random();
List <int> tempStore = new List<int>();
List <int> tempStore2 = new List<int>();
int tempValue = 0;
//int tempValue2 = 0;
int icount = 0;
int iMax = oSPListItemCollection.Count;
//int icount2 = 0;
//int iMax2 = oSPListItemCollection.Count;
SPListItem thisItem;
Label thisQuestion;
Label thisCorrectAnswer;
RadioButtonList thisAnswers;
while (icount < 10)
{
tempValue = rand.Next(1, iMax);
if (tempStore.Exists(value => value == tempValue))
continue;
else
{
tempStore.Add(tempValue);
thisQuestion = (Label) UpdatePanelMaster.FindControl("Question" + icount.ToString());
thisItem = oSPListItemCollection[tempValue];
thisQuestion.Text= thisItem["Question"].ToString();
//Inside loop to handle random answer arrangements
thisAnswers = (RadioButtonList) UpdatePanelMaster.FindControl("RadioButtonList" + icount.ToString());
//The following 4 lines of code populates the RadioButton List only in order on every row item run
//How to randomize the order?
thisAnswers.Items.Add(thisItem["CorrectAnswer"].ToString();
thisAnswers.Items.Add(thisItem["IncorrectAnswer1"].ToString();
thisAnswers.Items.Add(thisItem["IncorrectAnswer2"].ToString();
thisAnswers.Items.Add(thisItem["IncorrectAnswer3"].ToString();
thisCorrectAnswer = (Label) UpdatePanelMaster.FindControl("CorrectAnswer" + icount.ToString());
thisCorrectAnswer.Text= thisItem["CorrectAnswer"].ToString();
}
tempValue = 0;
icount++;
}
}
//End random question handling
catch (Exception ex)
{
throw ex;
}
}
Try using LINQ:
// start with an array of the question key strings
new[] { "CorrectAnswer", "IncorrectAnswer1", "IncorrectAnswer2", "IncorrectAnswer3" }
// for each key string s, select the relevant answer string
.Select(s => thisItem[s].ToString())
// sort by a random number (i. e. shuffle)
.OrderBy(s => rand.Next())
// convert to a List<T> (for the ForEach method)
.ToList()
// for each answer string s in the list, add s to thisAnswers.Items
.ForEach(s => thisAnswers.Items.Add(s));

Sorting and iterating widget starting from anywhere

List<Widget> list = List<Widget>();
Widget widgetA = new Widget();
Widget widgetB = new Widget();
Widget widgetC = new Widget();
Widget widgetD = new Widget();
Widget widgetE = new Widget();
Widget widgetF = new Widget();
list.AddRange(new[] { widgetA, widgetB, widgetC, widgetD, widgetE, widgetF });
What would I need to do to sort starting somewhere in the middle?
for example,
list.AddRange(new[] { widgetD, widgetE, widgetF, widgetA, widgetB, widgetC });
Or, in other words, start the index somewhere in the middle and then wrap the remaining objects in order.
You can use a combination of Skip, Take and Union.
List<Widget> widgets = ...
// now to split and wrap
int startIndex = 3;
var newList = widgets.Skip(startIndex)
.Union(widgets.Take(startIndex)
.ToList();
First sort them, then take first few elements and put them at the end.
var mid = 2; // starting point
var arr = new[] { widgetA, widgetB, widgetC, widgetD, widgetE, widgetF };
arr.Sort(); //you can use your own comparator for sorting
list.AddRange(arr.Skip(mid).Concat(arr.Take(mid)));

Categories