how to stop looping in file in c#? - c#

I'm Trying to save a customer in file but when i'm saving it, it keeps looping the person i entered i do not know where i'm doing it wrong .The problem is i'm thinking my logic is good but i know i'm doing something wrong somewhere which i can not find it .If you could help me i really appreciate it.
public partial class admin : Window
{
accounts acclist = new accounts();
customers cuslist = new customers();
public admin(string pin, accounts myacc, customers mycus)
{
InitializeComponent();
acclist = myacc;
cuslist = mycus;
}
public void saveaccount()
{
using (StreamWriter writer = new StreamWriter("account.txt"))
{
for (int i = 0; i < acclist.Count; i++)
{
var info = new List<string>
{
acclist[i].accounttype.ToString(),
acclist[i].PIN,
acclist[i].accountnumber,
acclist[i].accountbalance.ToString()
};
var account = String.Join(";", info);
writer.WriteLine(account);
}
}
}
//save to customer file
public void savefile()
{
using (StreamWriter writer = new StreamWriter("customer.txt"))
{
for (int i = 0; i < cuslist.Count; i++)
{
var info = new List<string>
{
cuslist[i].NAME.ToString(),
cuslist[i].pin,
};
var customer = String.Join(";", info);
writer.WriteLine(customer);
}
}
}
// add user
private void Sub_Click(object sender, RoutedEventArgs e)
{
customer newCus = new customer();
account newAcc= new account();
try
{
newCus.NAME = Nameadd.Text;
newCus.pin = pinadd.Text;
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'C';
for (int i = 0; i < acclist.Count; i++)
{
{
if(newAcc.accounttype == 'C')
{
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'S';
}
}
cuslist.add(newCus);
acclist.add(newAcc);
savefile();
saveaccount();
}
}
catch(Exception error)
{
MessageBox.Show(error.Message);
}
}
}

In your "save" event, your doing a for loop over the "count" of elements in your account list. That account list variable is global in scope to your class. The problem is that in your loop, you're adding to that list... so really the list you're iterating over is mutating right under-neath you. As you add that customer, the loop starts next iteration, checks the "count", and ends only when "i" is equal in value to the count. However each pass you're adding to the count... so technically you'll never reach the end as the accList.Count is constantly increasing by 1 each pass.
private void Sub_Click(object sender, RoutedEventArgs e)
{
customer newCus = new customer();
account newAcc= new account();
try
{
newCus.NAME = Nameadd.Text;
newCus.pin = pinadd.Text;
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'C';
for (int i = 0; i < acclist.Count; i++) // here you are checking acclist.Count... each iteration this increases by 1, thus i will never technically ever be equivalent to acclist.Count
{
{
if(newAcc.accounttype == 'C')
{
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'S';
}
}
cuslist.add(newCus);
acclist.add(newAcc); // <-- this is where you're adding to acclist each time. However inside this loop you're constantly increasing its size... thus your infinite loop you're hitting.
savefile();
saveaccount();
}
}
catch(Exception error)
{
MessageBox.Show(error.Message);
}
}
I suggest for one, you use a ForEach statement as it's been suggested. Also, use a separate list to hold your "new" accounts. If you need to add to acclist for whatever reason, then after you've added to your new list, iterate over that and add each one of those back to your acclist. This way you avoid mutating the very object you're looping over.
Another way is to first store the "count" into a variable and check against that so it never changes.
var myCounter = acclist.Count
for (int i = 0; i < myCounter ; i++)
{
...
But, I don't know which option is best for you as I obviously don't know the larger context of what it is you need to ultimately do. :) Either solution should stop your infinite loop though.

Consider the use of a foreach loop. This loop is best used for applications like the one in your savefile() method. It's great for applications where you want to perform an action for every item in a list or collection.
See here: https://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx?f=255&MSPPError=-2147217396
EDIT: The requested example:
List<string> FruitBasket = new List<string>();
FruitBasket.Add("apple");
FruitBasket.Add("banana");
FruitBasket.Add("orange");
foreach (string fruit in FruitBasket)
{
Console.WriteLine(fruit);
}
and this results in an output of:
apple, banana, orange.
In the foreach loop you need to declare a variable of the same type as your collection (so in this example I had a List of strings so I made my variable a string, and then they keyword in assigns this variable to each item in that collection, one at a time, starting from the beginning.

Related

How can I access multi-element List data stored in a public class?

My first question on SO:
I created this public class, so that I can store three elements in a list:
public class myMultiElementList
{
public string Role {get;set;}
public string Country {get;set;}
public int Commonality {get;set;}
}
In my main class, I then created a new list using this process:
var EmployeeRolesCountry = new List<myMultiElementList>();
var rc1 = new myMultiElementList();
rc1.Role = token.Trim();
rc1.Country = country.Trim();
rc1.Commonality = 1;
EmployeeRolesCountry.Add(rc1);
I've added data to EmployeeRolesCountry and have validated that has 472 lines. However, when I try to retrieve it as below, my ForEach loop only retrieves the final line added to the list, 472 times...
foreach (myMultiElementList tmpClass in EmployeeRolesCountry)
{
string d1Value = tmpClass.Role;
Console.WriteLine(d1Value);
string d2Value = tmpClass.Role;
Console.WriteLine(d2Value);
int d3Value = tmpClass.Commonality;
Console.WriteLine(d3Value);
}
This was the most promising of the potential solutions I found on here, so any pointers greatly appreciated.
EDIT: adding data to EmployeeRolesCountry
/*
Before this starts, data is taken in via a csvReader function and parsed
All of the process below is concerned with two fields in the csv
One is simply the Country. No processing necessary
The other is bio, and this itself needs to be parsed and cleansed several times to take roles out
To keep things making sense, I've taken much of the cleansing out
*/
private void File_upload_Click(object sender, EventArgs e)
{
int pos = 0;
var EmployeeRolesCountry = new List<myMultiElementList>();
var rc1 = new myMultiElementList();
int a = 0;
delimiter = ".";
string token;
foreach (var line in records.Take(100))
{
var fields = line.ToList();
string bio = fields[5];
string country = fields[4];
int role_count = Regex.Matches(bio, delimiter).Count;
a = bio.Length;
for (var i = 0; i < role_count; i++)
{
//here I take first role, by parsing on delimiter, then push back EmployeeRolesCountry with result
pos = bio.IndexOf('.');
if (pos != -1)
{
token = bio.Substring(0, pos);
string original_token = token;
rc1.Role = token.Trim();
rc1.Country = country.Trim();
rc1.Commonality = 1;
EmployeeRolesCountry.Add(rc1);
a = original_token.Length;
bio = bio.Remove(0, a + 1);
}
}
}
}
EDIT:
When grouped by multiple properties, this is how we iterate through the grouped items:
var employeesGroupedByRolwAndCountry = EmployeeRolesCountry.GroupBy(x => new { x.Role, x.Country });
employeesGroupedByRolwAndCountry.ToList().ForEach
(
(countryAndRole) =>
{
Console.WriteLine("Group {0}/{1}", countryAndRole.Key.Country, countryAndRole.Key.Role);
countryAndRole.ToList().ForEach
(
(multiElement) => Console.WriteLine(" : {0}", multiElement.Commonality)
);
}
);
__ ORIGINAL POST __
You are instantiating rc1 only once (outside the loop) and add the same instance to the list.
Please make sure that you do
var rc1 = new myMultiElementList();
inside the loop where you are adding the elements, and not outside.
All references are the same in your case:
var obj = new myObj();
for(i = 0; i < 5; i++)
{
obj.Prop1 = "Prop" + i;
list.Add(obj);
}
now the list has 5 elements, all pointing to the obj (the same instance, the same object in memory), and when you do
obj.Prop1 = "Prop" + 5
you update the same memory address, and all the pointers in the list points to the same instance so, you are not getting 472 copies of the LAST item, but getting the same instance 472 times.
The solution is simple. Create a new instance every time you add to your list:
for(i = 0; i < 5; i++)
{
var obj = new myObj();
obj.Prop1 = "Prop" + i;
list.Add(obj);
}
Hope this helps.

generate multiple class objects

I'm trying to generate multiple class objects but I get a "System.ArgumentOutOfRangeException: 'Index was out of range." message at this line: clients[i] = new IRCClient(credentials, textEdit1.Text);
public void FileRead()
{
if (File.Exists(AccountsFile))
{
Account.Clear();
using (StreamReader Reader = new StreamReader(AccountsFile))
{
string line;
while ((line = Reader.ReadLine()) != null)
{
Account.Add(new Accounts { Username = line.Split(':')[0], Password = line.Split(':')[1] });
}
}
}
else
{
File.Create(AccountsFile);
}
}
private void simpleButton1_Click(object sender, EventArgs e)
{
clients = new List<IRCClient>();
int num = 7;
foreach (var acc in Account)
{
for (int i = 0; i < num; i++)
{
credentials = new ConnectionCredentials(acc.Username, acc.Password);
clients[i] = new IRCClient(credentials, textEdit1.Text); //exception thrown
clients.Add(clients[i]);
foreach (var c in clients)
{
c.Connect();
}
}
}
}
List<T> is not the same as T[]. Ie Lists are not the same as arrays and have some different behaviours.
myarray[i] = someValue;
with arrays this will add some value to position i of the array. But remember arrays are initiliased up front
myarray = new object[10];
so as long as i >= 0 and < 10 the above code is fine.
Lists however are different and can (and in your case) are initialised empty. And then they get made bigger as and when needed. Its one of the benefits (but can also be a performance hit)
So when you do this:
clients = new List<IRCClient>();
you are creating an empty list, that has nothing in it so it has no indexes so when you do:
clients[i]
There is nothing at position 'i' so you get that exception.
The correct use would be
clients.Add(new IRCClient(credentials, textEdit1.Text))

Foreach going out of bounds while searching through array c#

The purpose of my program is to take a data txt file and edit it, and/or make additions and subtractions to it.
The text file format is like this:
Name|Address|Phone|# of people coming|isRSVP
The code I have seems to be doing it's job all the way up until I try to click one of the names within a listbox and it needs to search through the multidimensional array to pull information out and place within a set of textboxes where they can be edited. The problem is that the foreach loop I use gives me an out of bounds exception. I tried to do a step into debug to make sure the data is correct in the array and see the process. Everything seems to do correctly but for some reason in the conditional statement person[0]==listbox1.selectedIndex isn't returning true even though both are the same as I seen through the step into process. Any help would be greatly appreciated.
This is my code:
StringBuilder newFile = new StringBuilder();
static string txtList= "guest_list.txt";
static string[] file = File.ReadAllLines(txtList);
static int x = file.Count();
string[,] list = new string[x ,5];
public void loadGuestList()
{
int count2 = 0;
foreach (string line in file)
{
string[] sliced = line.Split('|');
int count = 0;
list[count2, count] = sliced[0];
count++;
list[count2, count] = sliced[1];
count++;
list[count2,count] = sliced[2];
count++;
list[count2,count]= sliced[3];
count++;
list[count2, count] = sliced[4];
count++;
listBox1.Items.Add(list[count2,0]);
count2++;
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (string person in list)
{
if ( person[0].ToString()==listBox1.SelectedItem.ToString())
{
addTxt.Text = char.ToString(person[1]);
textPhone.Text = char.ToString(person[2]);
textPeople.Text = char.ToString(person[3]);
if (person[4] == 'n' )
{
}
else
{
chkRSVP.Checked = true;
}
break;
}
}
}
The problem lies in this line:
foreach (string person in list)
The list is defined as being string[,] which when you for each over will do every element, not just the column of data. You really should do something such as:
for(int index = 0; index <= list.GetUpperBound(0); index++)
{
string slice1 = list[index, 0];
string slice2 = list[index, 1];
....
}
or switch to using a Dictionary<string, string[]>().
Try to use a "Person" object and override equals(). Right now you're trying to put your multidimensional array (list[0]) into a string, it'll give you a unwanted result. You should use list[0,0] instead.
In agreement with Adam Gritt, I tested the following code and it seemed to work:
using System;
namespace so_foreach_bounds
{
class MainClass
{
public static void Main (string[] args)
{
//System.Text.StringBuilder newFile = new System.Text.StringBuilder();
string txtList= "guest_list.txt";
string[] file = System.IO.File.ReadAllLines(txtList);
int x = file.Length;
System.Collections.Generic.List<string[]> list = new System.Collections.Generic.List<string[]> ();
foreach (string line in file)
{
string[] sliced = line.Split('|');
list.Add(sliced);
}
foreach (string[] person in list)
{
Console.WriteLine (String.Join(", ", person));
if (person[0] =="Gary")
{
string txtAdd = person[1];
string txtPhone = person[2];
string txtpeople = person[3];
if (person[4] == "n" )
{
}
else
{
bool has_resvped = true;
}
break;
}
}
}
}
}
The issue is how you are iterating over the 2d array. It is usually a better idea to create a "Person" class, than try to manage it via arrays though. Oh yes, and it's usually a good idea to check that a list box item is selected before attempting to use one of its members.

Add to array list from listbox selected item does not work properly

I have a program where I am trying to move items from one arrayList to another via a listbox and then print out the info in an XML, but the error I have is when I am adding it often certain times the values would repeat, when there are no repeats.
ArrayList list1 = new ArrayList();
ArrayList list2 = new ArrayList();
list1.Add(new RandomClass(var1, var2, var3, var4, var5, var6, var7));
foreach (object o in list1)
{
RandomClass m = (RandomClass)o;
selectionBox.Items.Add(m);
}
This is my initialization code.
bool req = true;
if (selectionBox.SelectedItem != null)
{
Count++;
errorLabel.Text = "";
for (int i = 0; i < selectionBox.Items.Count; i++)
{
if (selectionBox.GetSelected(i) == true)
{
RandomClass m = selectionBox.SelectedItem as RandomClass;
if (m.var2 == ((RandomClass)selectionBox.Items[i]).var2)
{
list2.Add(list1[i]);
}
}
}
}
else
{
errorLabel.Text = "Error";
}
Here is where I add to another array list. However as I said often the item would repeat and not be different, how can I resolve this problem?
Try clearing the second list each time you scan and add items from the first list.
list2.Clear();
for (int i = 0; i < selectionBox.Items.Count; i++)
....
I have fixed this problem using a list with my class, and there does not seem to be a problem.
List<RandomClass> list2 = new List<RandomClass>();
And then when adding I just simply put the following in the if statement
list2.Add(m);

Add Multiple record using Linq-to-SQL

I want to add Multiple rows into Table using Linq to SQL
public static FeedbackDatabaseDataContext context = new FeedbackDatabaseDataContext();
public static bool Insert_Question_Answer(List<QuestionClass.Tabelfields> AllList)
{
Feedback f = new Feedback();
List<Feedback> fadd = new List<Feedback>();
for (int i = 0; i < AllList.Count; i++)
{
f.Email = AllList[i].Email;
f.QuestionID = AllList[i].QuestionID;
f.Answer = AllList[i].SelectedOption;
fadd.Add(f);
}
context.Feedbacks.InsertAllOnSubmit(fadd);
context.SubmitChanges();
return true;
}
When I add records into list object i.e. fadd the record is overwrites with last value of AllList
I'm late to the party, but I thought you might want to know that the for-loop is unnecessary. Better use foreach (you don't need the index).
It gets even more interesting when you use LINQ (renamed method for clarity):
public static void InsertFeedbacks(IEnumerable<QuestionClass.Tabelfields> allList)
{
var fadd = from field in allList
select new Feedback
{
Email = field.Email,
QuestionID = field.QuestionID,
Answer = field.SelectedOption
};
context.Feedbacks.InsertAllOnSubmit(fadd);
context.SubmitChanges();
}
By the way, you shouldn't keep one data context that you access all the time; it's better to create one locally, inside a using statement, that will properly handle the database disconnection.
You should create object of Feedback in the scope of for loop, so change your method to :
public static bool Insert_Question_Answer(List<QuestionClass.Tabelfields> AllList)
{
List<Feedback> fadd = new List<Feedback>();
for (int i = 0; i < AllList.Count; i++)
{
Feedback f = new Feedback();
f.Email = AllList[i].Email;
f.QuestionID = AllList[i].QuestionID;
f.Answer = AllList[i].SelectedOption;
fadd.Add(f);
}
context.Feedbacks.InsertAllOnSubmit(fadd);
context.SubmitChanges();
return true;
}

Categories