Avoiding duplicates in List within loop - c#

I have a for loop which invokes a method within it. I add the return of this method to a list however I am getting duplicates in the list (the last return from the method call is all the items in the list). Presumably this is because the result object is the same instance. Is there a way around this?
IList<CarResult> carResults = new List<CarResult>();
for (int i = 0; i < cars.Count(); i++)
{
result = calculation.RunForCar(
engineSize[i],
yearOfManufacture[i],
carResults.Add(result);
}
return carResults;
}

I'm going to make a qualified guess and try to explain what's going on, without knowing exactly what's happening in your RunForCar().
Presumably this is because the result object is the same instance.
Probably yes.
Here's an example. It will not create new instances of Foo, but re-use the same instance over and over. So every time the name changes it changes the name on the reference. The list itself only contains the references, and therefore all the items in the list will be changed if you change the name on the reference.
var list = new List<Foo>();
var result = new Foo();
for(int i = 0; i < 5; i++)
{
result.Name = i.ToString();
list.Add(result);
}
foreach (var foo in list)
{
Console.WriteLine(foo.Name);
}
Output:
4
4
4
4
4
If we instead do like the code below, we assign result to a new reference, and then we leave the existing references untouched.
var list = new List<Foo>();
var result = new Foo();
for(int i = 0; i < 5; i++)
{
result = new Foo()
{
Name = i.ToString()
};
result.Name = i.ToString();
list.Add(result);
}
foreach (var foo in list)
{
Console.WriteLine(foo.Name);
}
Output:
0
1
2
3
4
Is there a way around this?
Yes, you can simply create a new instance of result for every loop. Without knowing more about either CarResult or RunForCar I cannot say when it's best to create the new instance. But here's an example:
IList<CarResult> carResults = new List<CarResult>();
for (int i = 0; i < cars.Count(); i++)
{
result = new CarResult();
result = calculation.RunForCar(
engineSize[i],
yearOfManufacture[i]); // Fixed type-o?
carResults.Add(result);
}
return carResults;
Alternatively you can have a local variable inside the loop.
IList<CarResult> carResults = new List<CarResult>();
for (int i = 0; i < cars.Count(); i++)
{
var result = new CarResult(); // Will not be accessible outside of loop.
result = calculation.RunForCar(
engineSize[i],
yearOfManufacture[i]); // Fixed type-o?
carResults.Add(result);
}
return carResults;

If the result is the same instance, you need to replace IList with HashSet

You need to create a new instance or result object on every pass of the loop in order to avoid adding it by reference to carResults list. Oterwise, all items in carResults will hold the reference to the same object which will contain the data from the last loop cycle.

Related

C# - An object is deleted from an array that holds a copy of the original

I hold an array in the following structure
List[] pageStore = new List[2];
Each cell of the array will contain a list of Page objects.
I wrote a function that ran in a loop and in each iteration creates a new list of Page.
I keep the list created in the above List.
Before each iteration I delete the contents of pages and the deletion is also done on the array itself.
How can this be prevented so that the array will keep the records I saved?
my code :
List<Page>[] pageStore = new List<Page>[2];
public void LoadExcel(Dictionary<string,string[]> FilePaths){
List<Page> pages = new List<Page>();
int pageStoreIndex = 0;
foreach (KeyValuePair<string, string[]> entry in
FilePaths) {
pages.Clear();
for (int i = 0; i < entry.Value.Length; i++) {
if (i == 0)
pages = fileManager.ParseExcelToObjects(excelDataSet,
ProjectTypeSelected.Name,entry.Key.Equals(Enum.Enums.ConnectorSide.COMBINED.ToString()) ? false : true);
...
...
}
if (pages.Count > 0)
pageStore[pageStoreIndex++] = pages;
}
}
page.Clear() cleared pageStore also.
You need to create a copy of the list.
pageStore[pageStoreIndex++] = new List<Page>(pages);
Like most .Net objects, List<T> is a reference type. Your line:
pageStore[pageStoreIndex++] = pages;
is just storing a reference to the single pages instance. It's not copying the values across. Every array entry is pointing at the same list.
As other answers have said, you need to create a new list each time if you want to keep separate lists:
List<Page>[] pageStore = new List<Page>[2];
public void LoadExcel(Dictionary<string,string[]> FilePaths)
{
int pageStoreIndex = 0;
foreach (KeyValuePair<string, string[]> entry in FilePaths)
{
//create a new list for each loop iteration
var pages = new List<Page>();
for (int i = 0; i < entry.Value.Length; i++)
{
//ps the following lines don't make sense. You've created a new List above,
//but ParseExcelToObjects() ignores it and returns its own list.
//So either your new List<Page>() is pointless, or this call is different in the real code.
if (i == 0)
pages = fileManager.ParseExcelToObjects(excelDataSet, ProjectTypeSelected.Name,entry.Key.EqualsEnum.Enums.ConnectorSide.COMBINED.ToString()) ? false : true);
...
...
}
if (pages.Count > 0)
pageStore[pageStoreIndex++] = pages;
}
}
You've assigned the value in pageStore to be the pages object. You'll need to create a new object using List<T>'s copy constructor:
if (pages.Count > 0)
pageStore[pageStoreIndex++] = new List<Page>(pages);

Creating new class instance in a loop

I've just started learning about methods and classes, I would like to know if I can have something like,
CarsSold Day1 = new CarsSold();
in a for loop where it will create a new instance of a new day each time it runs. For example on the second time the loop runs I want it to create an instance like this,
CarsSold Day2 = new CarsSold();
I have tried to use an array but either it cant be done with arrays or I'm using the wrong syntax. Thanks in advance.
Full code
class Program
{
static void Main(string[] args)
{
int[] weekDay = new int[7];
int userInput;
int x;
for (x = 0; x < weekDay.Length; x++)
{
Console.Write("Enter the number of cars sold: ");
bool ifInt = int.TryParse(Console.ReadLine(), out userInput);
CarsSold Day[x] = new CarsSold(userInput);
}
}
}
The problem is how you're trying to define your array. The syntax is invalid, and you're doing it in the wrong place.
You should define the array before your loop, and then only assign values to the array within the loop.
static void Main(string[] args)
{
int userInput;
CarsSold[] days = new CarsSold[7]; // create an array with 7 items
int x;
for (x = 0; x < days.Length; x++)
{
Console.Write("Enter the number of cars sold: ");
bool ifInt = int.TryParse(Console.ReadLine(), out userInput);
days[x] = new CarsSold(userInput); // assign a value to the days array at position x.
}
}
Note also that arrays start from 0, not 1, so the first item is actually days[0], and the last is days[6] (for a 7-item array). I took the liberty of removing weekDays since it wasn't needed here, and replaced it in the loop with days.Length.
Arrays can have set amount of things in them, so if you declare an array like this
object[] objectArray = new object[10];
Then that array can hold only 10 objects. If you want to add anything to an array you have to chose an index to which that thing will be assigned to, for example:
objectArray[2] = "some value";
in Your case you could iterate through the array and add new object to each index like this
for (var i = 0; i < objectArray.Length; i++)
{
objectArray[i] = new YourObject();
}
If the amount of objects you want to store is unknown and can change then you should use collections, for example a List
List<object> listOfObjects = new List<object>();
Now if you want to add anything to that list you simply do
listOfObjects.Add(itemYouWantToAddToTheList);
You access lists the same way you would access arrays, so you can use indexes like
var someValue = listOfObjects[0];
As you probably noticed this list has a generic parameter <object>, it tells the list what type of data it can store, in my example its the object type so it can pretty much store anything, but you can set it to string or int or any other type like your own class types and then this list would store only those types of objects.
If you don't know the number of days, then:
IList<CarsSold> soldCarsList = new List<CarsSold>();
foreach(var day in Days)
{
soldCarsList.Add(new CarsSold());
}
If you know the number of days(e.g:7), then:
CarsSold[] soldCarsArray = new CarsSold[7];
for (int i = 0; i < days.Length; x++)
{
soldCarsArray[i] = new CarsSold();
}

Updating a list within a list<class>

Since having a list<list<>> is bad practice, i have created a class containing 2 lists:
public class TouchSet
{
public List<DateTime> timeList = new List<DateTime>(ammountOfXValues);
public List<int> touchList = new List<int>(ammountOfXValues);
}
Then i have a function that is used to initialize the entire thing so i can use it further down the road:
public void initializeTouchDataListObject()
{
touchSetList = new List<DataStructure.TouchSet>(DataStructure.maxButtonsActive);
List<int> tempTouchList = new List<int>();
List<DateTime> tempTimeList = new List<DateTime>();
for (int a = 0; a < DataStructure.maxButtonsActive; a++)
{
DataStructure.TouchSet tempTouchSet = new DataStructure.TouchSet();
tempTouchSet.timeList = tempTimeList;
tempTouchSet.touchList = tempTouchList;
touchSetList.Add(tempTouchSet);
}
}
This is the loop where i add values to the list:
for (int i = 0; i < DataStructure.maxButtonsActive; i++)
{
if(touchSetList[i].timeList.Count == DataStructure.ammountOfXValues)
{
//RemoveAt removes at the given index within a list
touchSetList[i].timeList.RemoveAt(0);
touchSetList[i].touchList.RemoveAt(0);
//add
touchSetList[i].timeList.Add(DateTime.Now);
touchSetList[i].touchList.Add(temp);
}
else if(touchSetList[i].timeList.Count < DataStructure.ammountOfXValues)
{
//add
touchSetList[i].timeList.Add(DateTime.Now);
touchSetList[i].touchList.Add(temp);
}
else
{
int overLength = touchSetList[i].timeList.Count - DataStructure.ammountOfXValues;
//remove
touchSetList[i].timeList.RemoveRange(0, overLength + 1);
touchSetList[i].touchList.RemoveRange(0, overLength + 1);
//add
touchSetList[i].timeList.Add(DateTime.Now);
touchSetList[i].touchList.Add(temp);
}
}
The issue I'm facing is, that within a single pass through the for loop , it adds temp to every touchList not just the touchList of touchSetList[i].
For example after adding temp to touchSetList[i].touchList every other touchList also contains temp, not just the one where the index i applies to the class within the list.
I'm not sure why the List is behaving this way, and why it adds the value to every list not just the one with the corresponding index. I was under the impression that you can use an index to access a single item within a list. Any pointers or advice is appreciated.
public void initializeTouchDataListObject()
{
touchSetList = new List<DataStructure.TouchSet>(DataStructure.maxButtonsActive);
for (int a = 0; a < DataStructure.maxButtonsActive; a++)
{
List<DateTime> tempTimeList = new List<DateTime>();
List<int> tempTouchList = new List<int>();
DataStructure.TouchSet tempTouchSet = new DataStructure.TouchSet();
tempTouchSet.timeList = tempTimeList;
tempTouchSet.touchList = tempTouchList;
touchSetList.Add(tempTouchSet);
}
}
You don't create a new tempTimeList and tempTouchList for every new tempTouchSet, so they all get passed pointers to THE SAME list.
Do the initialization for tempTimeList and tempTouchList within your loop and you get a new one for every tempTouchSet.
Actually I'd redesign the entire thing. What I read from your code is that you want to store the timestamp of a touch event together with some information about the touch event.
So I'd design a class that contains all that data:
public class TouchInfo
{
public DateTime touchTime;
public int touchEvent;
}
Then you can easily store one list of touch events, instead having to keep two lists in sync.
List<TouchInfo> touchEvents = new List<TouchInfo>();
public void initializeTouchDataListObject()
{
for (int a = 0; a < DataStructure.maxButtonsActive; a++)
{
touchEvents.add(new TouchInfo());
}
}
In your initializeTouchDataListObject() method, you create tempTouchList and tempTimeList. Then, for EVERY TouchSet, you add these lists as their timeList and touchList. The problem here is that the two created lists are passed by REFERENCE, meaning that every single TouchSet has a reference to the two exact same lists. They all share the same lists and thus lists are changed for every TouchSet in every iteration.
You have 3 conditions. Add(temp) is in all 3.
1) a < b
2) a = b
3) a > c
​

After loop all elements same

I want to assign value to elements of my array. after running this, all elements of ListResults are same as last element of ListROI.
ListResults = new DataPoint[nROIrow];
DataPoint TempRes = new DataPoint();
System.Collections.ArrayList List = new System.Collections.ArrayList();
for (int i = 0; i < nROIrow; i++)
{
TempRes.X = ListROI[i].X;
TempRes.Y = ListROI[i].Y;
TempRes.u = dispROIcorr[i, 0];
TempRes.v = dispROIcorr[i, 1];
ListResults[i] = TempRes;
disp.Xpix = ListResults[i].X;
disp.Ypix = ListResults[i].Y;
disp.X = ListResults[i].X;
disp.Y = ListResults[i].Y;
disp.U = ListResults[i].u;
disp.V = ListResults[i].v;
List.Add(disp);
bSAVE.Enabled = true;
}
You only create a new DataPoint(); one time. So you end up with an array full of references to that same single instance.
The simple fix:
ListResults = new DataPoint[nROIrow];
//DataPoint TempRes = new DataPoint();
System.Collections.ArrayList List = new System.Collections.ArrayList();
for (int i = 0; i < nROIrow; i++)
{
DataPoint TempRes = new DataPoint();
...
ListResults[i] = TempRes;
var disp = new ...
disp.Xpix = ListResults[i].X;
....
List.Add(disp);
}
The problem with your code is that you are reusing the TempRes variable. When you perform the "List.Add" you are just adding a reference to it, and all these references are (obviously) the same. You also modify it, so each identical reference logically points to the same identical data.
Instead, write:
System.Collections.ArrayList List = new System.Collections.ArrayList();
for (int i = 0; i < nROIrow; i++)
{
DataPoint TempRes = new DataPoint();
...
Note also that ArrayList is generally considered to be deprecated since .NET 2.0 and you should be using List<T> instead.
do
disp = new ... // whatever
before assigning values to disp[i].???
actually what is happening is all the references in your List are referring to disp which is the single object that was created outside the for loop, hence all items in List are pointing to same disp object, hence same values.

For Loop: i in variable names

SubnetConvert SubnetOctet1 = new SubnetConvert();
SubnetConvert SubnetOctet2 = new SubnetConvert();
SubnetConvert SubnetOctet3 = new SubnetConvert();
SubnetConvert SubnetOctet4 = new SubnetConvert();
int Octet1 = int.Parse(txtOctet1.Text);
SubnetOctet1.OctetConvert = Octet1;
lblOctet1.Text = SubnetOctet1.SendBinary;
int Octet2 = int.Parse(txtOctet2.Text);
SubnetOctet2.OctetConvert = Octet2;
lblOctet2.Text = SubnetOctet1.SendBinary;
int Octet3 = int.Parse(txtOctet3.Text);
SubnetOctet3.OctetConvert = Octet3;
lblOctet3.Text = SubnetOctet1.SendBinary;
int Octet4 = int.Parse(txtOctet4.Text);
SubnetOctet4.OctetConvert = Octet4;
lblOctet4.Text = SubnetOctet1.SendBinary;
is it possible to put all this code in a For loop like
For (int i = 1; i <=4; i++)
{
SubnetConvert SubnetOctet[i] = new SubnetConvert();
int Octet[i] = int.Parse(txtOctet[i].Text);
SubnetOctet[i].OctetConvert = Octet[i];
lblOctet[i].Text = SubnetOctet[i].SendBinary;
}
I have tried the coding above and it doesn't work, I have just put it there for an example of what I want to achieve
The code sample is not something possible - there is no support for control arrays as you have shown.
A better way would be to write a function that encapsulates the repeating code and pass in the differing parameters.
private void SetBinaryValue(string value, Label display)
{
int Octet = int.Parse(value);
SubnetOctet.OctetConvert = Octet;
display.Text = SubnetOctet.SendBinary;
}
You would call this function like so:
SetBinaryValue(txtOctet1.Text, lblOctet1);
SetBinaryValue(txtOctet2.Text, lblOctet2);
Note that you only need one SubnetConvert with this approach (which you can either initialize within the function, or as a field).
It's perfectly possible to loop through named controls using FindControl:
var subnetOctet = new SubnetConvert();
for (int i = 1; i <= 4; ++i) {
// ID suffix as string
var indexText = i.ToString(CultureInfo.InvariantCulture);
// ID of TextBox and Label
var textBoxId = "txtOctet" + indexText;
var labelId = "lblOctet" + indexText;
// The TextBox and the Label
var textBox = (TextBox)FindControl(textBoxId);
var label = (Label)FindControl(labelId);
// Parse the value into an int
int octet = int.Parse(textBox.Text);
subnetOctet.OctetConvert = octet;
// Update the TextBox's Test
label.Text = subnetOctet.SendBinary;
}
One advantage to using this method is that you can add more controls on the fly, or even programmatically, and if you keep track of the number of subnets you need to handle, you do not have to update your code.
You could also create an Array with the your objects as the elements and then loop through the array and execute the functions based on the array position at loop position;
Dog pet1 = new Dog();
Dog pet2 = new Dog();
Dog pet3 = new Dog();
Dog pet4 = new Dog();
//create a list of pets and add your pets to them
List<Dog> pets = new List<Dog>();
pets.Add(pet1);
pets.Add(pet2);
pets.Add(pet3);
pets.Add(pet4);
//Using a for each loop to go through each element in the array and execute identical actions on each
//element
foreach (Dog pet in pets)
{
pet.SetName("Fido");
}
//or create a for each loop that will allow you to know the position
//you are currenly at in the arry as the integer of i increments in the loop
for (int i = 0; i <= pets.Count; i++)
{
pets[i].SetName("Fido");
}
Ideally what you will want to do is create a single object and insert multiple instances of the object into the list via another loop and then use the foreach or the for loop to access an element of the list to manipulate a singular instance.
Dog dog = new Dog();
//create a list of pets and add your pets to them
List<Dog> pets = new List<Dog>();
for (int i = 0; i <= 5; i++)
{
pets.Add(dog);
}
//Using a for each loop to go through each element in the array and execute identical actions on each
//element
foreach (Dog pet in pets)
{
pet.SetName("Fido");
}

Categories

Resources