C# List Manipulation - Output Different When Stepping Through vs. Running - c#

I'm a C# newbie and I'm really confused about something I'm trying to do for a project in a C# class.
The assignment is some list manipulation in C#.
The program accepts a list of items in the text box, then iterates through those items, creating multiple copies of the list. It randomly resizes each copy of the list to between 3 and all items. It then outputs all the copies.
The problem I'm having is that when I step through this program with the debugger, I get the expected output. The same happens if I display a message box after each iteration (as I have in the code below).
However, if I just run the program straight through, I get a different output. Instead of variations in the lists, all the copies of the list are exactly the same.
If you see in the code I've commented "// FIRST DEBUG MESSAGEBOX" and "// SECOND DEBUG MESSAGEBOX". If the first debug messagebox code is left in there, the output is as expected...multiple versions of the list are output with random lengths between 3 and all items.
However, and this is where I'm confused...if you comment out the first debug messagebox code, you get a different result. All versions of the list output are the same length with no variation.
Any help would be appreciated! Here's the code I have so far...sorry if it's terrible - I'm new at C#:
public partial class MainForm : Form
{
/**
* Vars to hold raw text list items
* and list items split by line
*/
String rawListItems = "";
List<string> listItems = new List<string>();
List<List<string>> textListItems = new List<List<string>>();
public MainForm()
{
InitializeComponent();
}
private void cmdGo_Click(object sender, EventArgs e)
{
// store the contents of the list item text box
this.rawListItems = txtListItems.Text;
this.listItems.AddRange(Regex.Split(this.rawListItems, "\r\n"));
// setup min and max items - max items all items
int minItems = 3;
int maxItems = this.listItems.Count;
// We'll copy this list X times, X = same number of items in list
for (int i = 0; i < this.listItems.Count; i++)
{
// make a copy of the list items
List<string> listItemsCopy = new List<string>(this.listItems);
// get a random number between min items and max items
Random random = new Random();
int maxIndex = random.Next(minItems, maxItems + 1); // max is exclusive, hence the +1
// remove all elements after the maxIndex
for (int j = 0; j < listItemsCopy.Count; j++)
{
if (j > maxIndex)
{
listItemsCopy.RemoveAt(j);
}
}
// add the list copy to the master list
this.textListItems.Add(listItemsCopy);
// FIRST DEBUG MESSAGEBOX
String tst = "";
foreach (string item in listItemsCopy)
{
tst += item + " ## ";
}
MessageBox.Show(tst);
}
// SECOND DEBUG MESSAGEBOX
String output = "";
foreach (List<string> listitem in this.textListItems)
{
foreach (string item in listitem)
{
output += item + " ## ";
}
}
MessageBox.Show(output);
}
}

Move the creation of Random out of the loop:
Random random = new Random();
By default, the constructor uses a default time based seed. In a tight loop, you may be getting 'the same' random generator instead of a different one with each loop.
When using MessageBoxes or single stepping, you are allowing the timer to run and getting 'a new' random generator in each loop.

I don't understand your assignment exactly, but this loop seems to be incorrect:
for (int j = 0; j < listItemsCopy.Count; j++)
{
if (j > maxIndex)
{
listItemsCopy.RemoveAt(j);
}
}
when you remove an element in the middle of a list, elements after that get shifted, so not all the elements after maxIndex get removed, as you might expect.

In circumstances where stepping through the code in a debugger affects the behaviour of the program, a useful alternative debugging technique is to use the System.Diagnostics namespace in particular the Trace class.
The Trace functions work much like Console.WriteLine(), you can trace a string or a format string plus an array of objects to populate the format string, e.g.:
Trace.TraceInformation("some message that tells me something");
Trace.TraceInformation("some useful format string {1}, {0}",
new object[] {someObject, someOtherObject});

Related

Unity C# Cycling some texts without repeating them

I've a line in the bottom part of the main screen of the game, that every time the scene is loaded, it shows a different tip (how to play, how to change music...).
The question is that I'm using Random.Range for that but, honestly, I'll prefer a way where all tips are showed, one by one in a random way, but without repeating any of them.
My code is as follows:
int randNum;
void Start () {
randNum = Random.Range(0,5);
}
void Update () {
switch (randNum)
{
case 0:
// blah, blah, blah...
case 1...
How can I achieve what I want?
Thans for yout timeeee :)
You can remove the switch statement and store each message in a list.
var tips = new List<string>();
tips.Add("The clouds are white");
...
Then you can randomize the elements in the list (more on that here) and show tips one by one. All you need is to keep track of the index. Example:
// This needs to be a field.
int index = 0;
void ShowTip()
{
// TODO: make sure index is not equal or greater than tips.Count
tip = tips[index];
index++;
// Display the tip
}
What you can do is to shuffle your list of tip. The Fisher-Yates shuffle is one of the most common.
static Random _random = new Random();
static void Shuffle<T>(T[] array)
{
int n = array.Length;
for (int i = 0; i < n; i++)
{
// Use Next on random instance with an argument.
// ... The argument is an exclusive bound.
// So we will not go past the end of the array.
int r = i + _random.Next(n - i);
T t = array[r];
array[r] = array[i];
array[i] = t;
}
}
public static void Main()
{
string[] array = { "tip 1", "tip 2", "tip 3" };
Shuffle(array);
foreach (string value in array)
{
Console.WriteLine(value);
}
}
output
net
dot
perls
source
Suppose your messages are stored in a list of string declared at the global level together with your random class and with an additional List of strings initially empty
List<string> needToDisplayMessages = new List<string>();
List<string> base_messages = new List<string>{"message1","message2","message3","message4","message5"};
Random rnd = new Random();
In your update method check if the list of messages to be displayed is empty and if yes copy the messages from the list with the predefined message. Now use the random instance to choose the index of the message to display and get it from the dynamic list. Finally remove the message from the list of message still to be displayed.
void Update () {
// We refill the list if it is empty
if(needToDisplayMessages.Count == 0)
needToDisplayMessages.AddRange(base_messages);
// Choose a random number topped by the count of messages still to be displayed
int index = rnd.Next(0, needToDisplayMessages.Count);
string message = needToDisplayMessages[index];
..... display the message someway .....
// Remove the message from the list
needToDisplayMessages.RemoveAt(index);
}
Of course, if you want to display the messages in sequential order there is no need of this but (as already explained) just an index. But if you want to randomly choose the message until you have shown all of them and then restart the cycle, perhaps this approach is not too much complex.

IndexOutOfRange Exception Thrown on Setting Int value to Array

On my app a teacher can have several classes, and when I exclude a teacher's profile I have to, first, delete his classes. I'm trying to put each class_id of this teacher on an int array, to later delete all classes which the id is contained inside this array.
This is my code so far:
int x = 0;
int[] count = new int[x];
while (reader_SelectedClasses.Read())
{
if(x != 0)
{
x++;
count = new int[x];
}
count[x] = _class.Class_id = reader_SelectedClasses.GetInt16("class_id");
}
And this is what
reader_SelectedClasses.Read()
does:
select class_id from tbl_class where user_id = " + id + ";
And this is the return, when I try this on MySQL:
But it gives me back an IndexOutOfRangeException when I run the code on my DAO class. What am I missing? Already went here but didn't quite understand. Can someone please explain on few words and post and fixed code for this?
You need to learn how to use a debugger and step through your program.
count = new int[x]; discards what was in count and creates a new array that contains zeroes. This array's indexes go from 0 to x - 1.
count[x] = ... sets the array element at index x which according to the previous line is one past the end of the array.
You need to set count = new int[x] only once, at the beginning of your program, and set count[x] = ... only if x >= 0 and x < count.Length.
You are getting IndexOutOfRange Exception because you are trying to access element from array which is out of range.
At first line you are setting x = 1. Hoping that controller enters while loop and as x is 1 it doesn't enter if loop and it executes next statement. But count[1] (x = 1) is not allowed as you have created array with only one element and that you can access with count[0]. (Array indexing starts from 0)
You are trying to achieve a List behavior with an array.
Obviously, the IndexOutOfRangeException is because you initialize an empty array and then try to add values to it in a non-existing cell.
Try to convert to List<int>:
List<int> count = new List<int>();
while (reader_SelectedClasses.Read())
{
int classId = _class.Class_id = reader_SelectedClasses.GetInt16("class_id");
count.Add(classId);
}
If you really need an array out of it you can do:
int[] countArray = count.ToArray()

picking random name from text file in c# windows forms non repeatable

I was wondering how get on with this code, currently working on a tournament bracket system.
Currently I have created a comboBox that fetches all the lines from "log.txt" there are 16 lines in the txt file; then I created a assign button that is supposed to assign all the names into 16 textboxes called User1 --> User16, however the same name cant be repeated.
I looked at "Array of list" & "Array of string", but I seem to be stuck since I cant really figure out what to put in the code.
my random button looks like this at the moment:
private void assign_Click(object sender, EventArgs e)
{
int x;
Random rnd = new Random();
x = rnd.Next(0, 16);
User1.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User2.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User3.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User4.Text = comboBox2.Items[x].ToString();
and so on untill i hit
x = rnd.Next(0, 16);
User16.Text = comboBox2.Items[x].ToString();
}
One of the simplest, but not necessarily most efficient, way to do this is to put all your strings into a List<string> and remove them randomly one-by-one. This would work a lot better if you put all your textboxes into a collection as well. For example, given a list of strings called myStrings and a collection of textboxes called myTextboxes, you could:
for (var i=0; i < myStrings.Count; i++)
{
var idx = rnd.Next(0, myStrings.Count);
myTextboxes[i].Text = myStrings[idx]; // Note: we are assuming the two collections have
// the same length
myStrings.RemoveAt(idx);
}
This is very easy to implement and very easy to get right, but it's not terribly efficient (for 16 items, it probably doesn't matter) because your collection is repeatedly resized. For a more efficient approach, first shuffle your strings using the Fisher-Yates shuffle and then just assign the first entry from your shuffled strings to the first textbox, the second to the second, and so on.
You could use a List, and after each assignment you could remove the assigned item from the list. This would prevent duplicates.
http://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx
How about removing each item after selecting it?
Try something like
comboBox1.Items.RemoveAt(x);
After adding it and each time your
x = rnd.Next(0, 16);
code will reduce to
x = rnd.Next(0, 15);
until it reaches zero.
A different approach would be after selecting one randomly loop through all the filled ones (or all in general for simpler code) and check if it is already selected. If already selected get a new one until it's different.
For that you could use an array of textboxes (store what you have in an array) and loop through them like so
for(int i=0;i<16;i++)
if(textBoxArray[i].Text==comboBox2.Items[x].toString()){
chosen=true;
}
But removing them from the combobox is much simpler and much faster as code. If you want them to still appear in your combobox you could simultaneously in a List, get your items from that List and remove it from there.
The user will not see anything.
To accomplish this, is fairly simple.
First, you know there are 16 items in total. You don't need to randomize the list but rather, randomize the index that you use to access the item of the list. This part you know.
In order to avoid repeating items, you need to keep a list of indexes that have already been used. Once you have determined an unused index, that's when you need to access your list.
Example:
class Sample
{
List<int> _usedIndexes;
public Sample()
{
_usedIndexes = new List<int>();
}
public int GetRandomIndex(int s, e)
{
Random rnd = new Random();
//Initialize with a random number
int x = rnd.Next(s, e);
//While the index exists in the list of used indexes, get another random number.
while(_usedIndexes.Exists(index => index == x))
{
x = rnd.Next(s, e);
}
//Add the number to the list of used indexes
_usedIndexes.Add(x);
return x;
}
}
Then you simply access the List of names you have with the index you have acquired as follows:
int unusedIndex = GetRandomIndex(0, 16);
User1.Text = comboBox2.Items[unusedIndex].ToString();

C# Best way to parse flat file with dynamic number of fields per row

I have a flat file that is pipe delimited and looks something like this as example
ColA|ColB|3*|Note1|Note2|Note3|2**|A1|A2|A3|B1|B2|B3
The first two columns are set and will always be there.
* denotes a count for how many repeating fields there will be following that count so Notes 1 2 3
** denotes a count for how many times a block of fields are repeated and there are always 3 fields in a block.
This is per row, so each row may have a different number of fields.
Hope that makes sense so far.
I'm trying to find the best way to parse this file, any suggestions would be great.
The goal at the end is to map all these fields into a few different files - data transformation. I'm actually doing all this within SSIS but figured the default components won't be good enough so need to write own code.
UPDATE I'm essentially trying to read this like a source file and do some lookups and string manipulation to some of the fields in between and spit out several different files like in any normal file to file transformation SSIS package.
Using the above example, I may want to create a new file that ends up looking like this
"ColA","HardcodedString","Note1CRLFNote2CRLF","ColB"
And then another file
Row1: "ColA","A1","A2","A3"
Row2: "ColA","B1","B2","B3"
So I guess I'm after some ideas on how to parse this as well as storing the data in either Stacks or Lists or?? to play with and spit out later.
One possibility would be to use a stack. First you split the line by the pipes.
var stack = new Stack<string>(line.Split('|'));
Then you pop the first two from the stack to get them out of the way.
stack.Pop();
stack.Pop();
Then you parse the next element: 3* . For that you pop the next 3 items on the stack. With 2** you pop the next 2 x 3 = 6 items from the stack, and so on. You can stop as soon as the stack is empty.
while (stack.Count > 0)
{
// Parse elements like 3*
}
Hope this is clear enough. I find this article very useful when it comes to String.Split().
Something similar to below should work (this is untested)
ColA|ColB|3*|Note1|Note2|Note3|2**|A1|A2|A3|B1|B2|B3
string[] columns = line.Split('|');
List<string> repeatingColumnNames = new List<string();
List<List<string>> repeatingFieldValues = new List<List<string>>();
if(columns.Length > 2)
{
int repeatingFieldCountIndex = columns[2];
int repeatingFieldStartIndex = repeatingFieldCountIndex + 1;
for(int i = 0; i < repeatingFieldCountIndex; i++)
{
repeatingColumnNames.Add(columns[repeatingFieldStartIndex + i]);
}
int repeatingFieldSetCountIndex = columns[2 + repeatingFieldCount + 1];
int repeatingFieldSetStartIndex = repeatingFieldSetCountIndex + 1;
for(int i = 0; i < repeatingFieldSetCount; i++)
{
string[] fieldSet = new string[repeatingFieldCount]();
for(int j = 0; j < repeatingFieldCountIndex; j++)
{
fieldSet[j] = columns[repeatingFieldSetStartIndex + j + (i * repeatingFieldSetCount))];
}
repeatingFieldValues.Add(new List<string>(fieldSet));
}
}
System.IO.File.ReadAllLines("File.txt").Select(line => line.Split(new[] {'|'}))

Dynamic UI, Loop

I created a dynamic UI that shows each module that the user has added to their selection and all the assessments for that module underneath the module name,
All data is saved in array lists - including objects as textBoxes and labels.
Here's what it looks like:
http://www.4shared.com/photo/fullsize/Ja3jHpRJ/screen1.html
Then the user should enter their marks for each assessments
And the Calculate button should calculate their overall mark for each module
At this stage I am just adding the marks together.
The program works fine when there's only one module added, but when there are more modules it seems to think that a TextBox is empty, even though i fill them all with numbers.
here is my code
private void calculateButton_Click(object sender, EventArgs e)
{
int modLength = dat.ModuleTitle.Count;
int modAsses = 0;
int loopCount = 0;
for (int i = 0; i < modLength; i++)
{
int total = 0;
if ((textboxComputer[loopCount] as TextBox).Text == "")
{
Console.WriteLine("!!!!!");
}
else
{
modAsses = int.Parse(dat.ModuleAssessNum[i].ToString());
for (int y = 0; y < modAsses; y++)
{
total = total + int.Parse((textboxComputer[loopCount] as TextBox).Text.ToString());
loopCount = loopCount + 1;
}
Console.WriteLine(total);
}
}
}
When i add two or more modules it displays the "!!!!!" message, but calculates the mark for the first module added.
here's some output
http://www.4shared.com/photo/fullsize/PlY29BMQ/screen2.html
You need to make sure that the code that creates the extra dynamic elements on screen is being called as part of the page load, so that then the viewstate is inserted back into the elements and can be read back out by your code.
there is something wrong with your code, probably a lot wrong(except the names of your variables).
Take a look at your loopCount variable - you will only conditionally change it in your loop but you always use it at the start of the loop(where you do textboxComputer[loopCount]).
So what's the expected behavior of that? The first time you get in to the loop if you go to your 'else' part you may print the total as you say but you also change the loopCount variable, once you get to the Console.WriteLine("!!!!!"); you never change it again - so your iterations will behave the same.

Categories

Resources