ObservableCollection adds an item and overrides the others - c#

I have this function:
ObservableCollection<TankstellenItem> completeList = new ObservableCollection<TankstellenItem>();
for(var j = 0; j < listItem.Count; j++)
{
foreach (FuelItem fItem in listItem.ToList()[j].Fuels)
{
Debug.WriteLine("HERE AGAIN: " + fItem.Price);
TankstellenItem newItem = new TankstellenItem();
ObservableCollection<FuelItem> fuelList = new ObservableCollection<FuelItem>();
newItem = listItem.ToList()[j];
newItem.Fuels = null;
fuelList.Add(fItem);
newItem.Fuels = fuelList;
completeList.Add(newItem);
Debug.WriteLine("PRICES: " + completeList.ToList()[0].Fuels[0].Price);
}
}
Debug.WriteLine("COMPLETELIST LENGTH: " + completeList.ToList()[0].Fuels[0].Price + " + " + completeList.ToList()[1].Fuels[0].Price);
and the outcome is:
LISTITEM LENGTH: 1
HERE AGAIN: 1,699
PRICES: 1,699
HERE AGAIN: 1,529
PRICES: 1,529
COMPLETELIST LENGTH: 1,529 + 1,529
As you can seein the foreach the Prices are different. But after the for-method the output is only 1,529 in every item. I don't understand why this happens.

From what I can tell from your code, you want to retrieve all the Fuel objects from each TankstellenItem in your original list (listItem) and then put them into an ObservableCollection.
// Create Complete List
ObservableCollection<TankstellenItem> completeList = new ObservableCollection<TankstellenItem>();
// Loop through each TankstellenItem in listItem
foreach(TankstellenItem tItem in listItem) {
// Create an ObservableCollection to store FuelItem objects
ObservableCollection<FuelItem> fuelList = new ObservableCollection<FuelItem>();
// Loop through each FuelItem in the current TankstellenItem
foreach(FuelItem fItem in tItem) {
// Add the FuelItem from current TankstellenItem to the ObservableCollection
Debug.WriteLine("HERE AGAIN: " + fItem.Price);
completeList.Add(new TankstellenItem() {
// Add the FuelItem
Fuels = new ObservableCollection<FuelItem>() {
fItem,
};
}
}
}
Although, it might be easier, if you only care about the FuelItems, to have a collection of the FuelItems instead of wrapping them inside a TankstellenItem.
ObservableCollection<FuelItem> completeList = new ObservableCollection<FuelItem>();
foreach(TankstellenItem tItem in listItem) {
foreach(FuelItem fItem in tItem) {
completeList.Add(fItem);
}
}

Linq might make this easier: Do you actually want
var completelist=listItem.SelectMany(
i=>i.Fuels.Select(
f=>new TankstellenItem { Fuels=new ObservableCollection<FuelItem>(new[] { f})}
)
).ToList();

Related

Why a method is modifying a List<string> variable in my Load Page?

I want to create two List with methods, the first method create a new list, and the second modify the first one but return a new one. For example, the first list has 200 items, and after add and delete some items in the second method, the returned one has 120 items.
But the second method is actually modifying the first list (now both list has 120 items).
What am I doing wrong?.
PD. I'm learning
protected void Page_Load(object sender, EventArgs e)
{
List<string> firstList = OEEClass.GetCompleteListDocument(DateTime.Today, "BZX"); // Let say it has 200 items
List<string> modifiedList = OEEClass.ModifyList(firstList); // The returned list has less items
}
public class OEEClass
{
public static List<string> GetCompleteListDocument(DateTime Fecha, string noMaquina)
{
var rawDoc = new HtmlDocument();
var tempList = new List<string>();
string url = #"C:/www/WPCS-Feedback/" + Fecha.Year + "/" + Fecha.Month + "/" + Fecha.Day.ToString("d2") + "/" + "Production state data.htm";
if (File.Exists(url) == true)
{
rawDoc.Load(url);
string cleanString = rawDoc.DocumentNode.InnerText.Trim().Replace(" ", "");
cleanString = Regex.Replace(cleanString, #"^\s+$[\r\n]*", string.Empty, RegexOptions.Multiline);
tempList = Regex.Split(cleanString, "\r\n|\r|\n").ToList();
tempList.RemoveRange(0, 5);
for (int j = 0; j < tempList.Count; j++)
{
if (tempList[j].StartsWith("ProductionTerminated") || tempList[j].StartsWith("ProductionInterrumpted"))
{
tempList.Insert(j + 4, "PressSingleCycleActivated=0");
}
}
}
return tempList;
}
public static List<string> ModifyList(List<string> completeListDocument)
{
for (int i = 0; i < completeListDocument.Count; i++)
{
if (completeListDocument[i].StartsWith("MachineSetup"))
{
completeListDocument.RemoveRange(i, 6);
i--;
}
}
return completeListDocument;
}
}
The simplest thing you can do is make a copy of your list before modifying it:
public static List<string> ModifyList(List<string> completeListDocument)
{
var results = new List<string>(completeListDocument);
for (int i = 0; i < results.Count; i++)
{
if (results[i].StartsWith("MachineSetup"))
{
results.RemoveRange(i, 6);
i--;
}
}
return results;
}

Why is the .Clear() function clearing the wrong list?

public void ConvertMoves()
{
for (int i = 0; i < maxDirections; i++)
{
Debug.Log("gimme tsMoves "+tSpossibleMoves[i].Count + " from " + this);
possibleAttacks[i] = tSpossibleAttacks[i];
possibleAttacksInactive[i] = tSpossibleAttacksInactive[i];
possibleAttackIndicators[i] = tSpossibleAttackIndicators[i];
possibleMoves[i] = tSpossibleMoves[i];
Debug.Log("Gimme moves(1) " + possibleMoves[i].Count + " from " + this);
}
for (int i = 0; i < maxDirections; i++)
{
tSpossibleAttacks[i].Clear();
tSpossibleAttacksInactive[i].Clear();
tSpossibleAttackIndicators[i].Clear();
tSpossibleMoves[i].Clear();
Debug.Log("Gimme moves(2) " + possibleMoves[i].Count + " from " + this);
}
}
so the Debug Log reports the following:
gimme tsMoves 2 from JeanArc(Clone) (JeanArc)
Gimme moves(1) 2 from JeanArc(Clone) (JeanArc)
sofar everything is doing fine but then...
Gimme moves(2) 0 from JeanArc(Clone) (JeanArc)
why does it clear the moves of whole different List variable ?
This doesn't create a copy of the list item:
possibleAttacks[i] = tSpossibleAttacks[i]
It simply copies the reference to the same object into a second variable, so possibleAttacks[i] and tSpossibleAttacks[i] now both point to the same item in memory. Think of it like having two credit cards to access one bank account.
You can read more about reference types here in Microsoft's docs.
As Heinzi pointed out in the comment below, you can copy your item (as it's a list) by calling:
possibleAttacks[i] = tSpossibleAttacks[i].ToList();
By the way, if you just want to assign tSpossibleAttacks[i] and then reset it, you could also just do this:
possibleAttacks[i] = tSpossibleAttacks[i];
tSpossibleAttacks[i] = new List<your_type_name_here>(); // this will overwrite the reference held by `tSpossibleAttacks[i]`.
Note that if your list contains reference types, you have a similar problem within the list, for example:
public class Test
{
public string Name { get; set; }
}
List<Test> list1 = new List<Test>();
list1.Add(new Test() { Name = "John" });
List<Test> list2 = list1.ToList();
Console.WriteLine(list1[0].Name); // John
Console.WriteLine(list2[0].Name); // John
list2[0].Name = "Fred";
Console.WriteLine(list1[0].Name); // Fred
Console.WriteLine(list2[0].Name); // Fred
So I'd recommend reading up on value types vs reference types and how references work in C#.
What #John said. You need to copy the lists.
for (int i = 0; i < maxDirections; i++)
{
Debug.Log("gimme tsMoves "+tSpossibleMoves[i].Count + " from " + this);
possibleAttacks[i] = tSpossibleAttacks[i];
tSpossibleAttacks[i] = new List<T>;
possibleAttacksInactive[i] = tSpossibleAttacksInactive[i];
tSpossibleAttacksInactive[i] = new List<U>();
possibleAttackIndicators[i] = tSpossibleAttackIndicators[i];
tSpossibleAttackIndicators[i] = new List<V>();
possibleMoves[i] = tSpossibleMoves[i];
tSpossibleMoves[i] = new List<Z>();
Debug.Log($"Gimme moves(1), i={i}: {possibleMoves[i].Count} from {this}");
Debug.Log($"Gimme moves(2) i={i}: {tpossibleMoves[i].Count} from {this}");
}
Example:
var l1 = new List<string>();
List<string> l2;
l1.Add("One");
l1.Add("Two");
l2 = l1;
l1 = new List<string>();
l1.Add("Three");
Console.WriteLine("L1:");
foreach (var elem in l1)
{
Console.WriteLine(elem);
}
Console.WriteLine("L2:");
foreach (var elem in l2)
{
Console.WriteLine(elem);
}
This prints:
L1:
Three
L2:
One
Two

c# dynamically modifying list

I don't understand how to do it with foreach...
The goal is to modify a list each time we change Num.
Is the way with a Canvas List and a working List is ok for nice coding?
class Program
{
static void Main(string[] args)
{
int i_Num = 0;
string Str_Num = "";
string[] linkToPLC = {"toto[{0}].test{1}", "tata[{0}].test{1}", "titi[{0}].test{1}"};
List<string> genlnkPLCCanvas = new List<string>(linkToPLC);
List<string> genlnkPLCworkingwith = new List<string>(linkToPLC);
Console.WriteLine("Insert Num: ");
Str_Num = Console.ReadLine();
i_Num = Convert.ToInt32(Str_Num);
for (int item = 0; item < genlnkPLCCanvas.Count; item++)
{
genlnkPLCworkingwith[item] = String.Format(genlnkPLCworkingwith[item], i_Num, 200);
Console.WriteLine("with List: result= " + genlnkPLCworkingwith[item]);
}
//foreach (string item in genlnkPLCCanvas) genlnkPLCworkingwith[item] = String.Format(item, i_Num, 200);
Console.ReadKey();
}
}
If you want to modify the existing list, you have to use for loop instead of foreach one:
foreach (var item in list) ...
should be changed into
for (int i = 0; i < list.Count; ++i) {
var item = list[i]; // not necessary, but often convenient
...
list[i] = ... // modification
...
}
For instance
for (int i = 0; i < genlnkPLCCanvas.Count; ++i) {
var item = genlnkPLCCanvas[i];
genlnkPLCCanvas[i] = string.Format(item, i_StationNum, 200);
}
When testing try creating reports (put all the logic into the single readable query) and then printing them out in one go:
...
var withListReport = genlnkPLC
.Select(item => "with List: result = " + string.Format(item, i_StationNum, 200));
var withoutListReport = genlnkPLC
.Select(item => "without List: result = " + string.Format(item, i_StationNum, 200));
// now you can do whatever you want with the reports:
// - print them to console
// Console.WriteLine(string.Join(Envrironment.NewLine, withListReport));
// - save to file:
// File.WriteAllLines(#"C:\MyFile.txt", withListReport);
// - print to, say, WinForm UI:
// MyTextBox.Text = string.Join(Envrironment.NewLine, withListReport);
Console.WriteLine(string.Join(Envrironment.NewLine, withListReport));
Console.WriteLine(string.Join(Envrironment.NewLine, withoutListReport));
Console.ReadKey();
String.Format() returns a string, it doesn't change whatever you're formatting. Therefore, your first foreach (var item in genlnkPLC) creates temporary strings that are immediately destroyed.
foreach (var item in genlnkPLC)
{
Console.WriteLine("with List = " + String.Format(item, i_StationNum, 200));
}
In the statement
foreach (var item in genlnkPLC)
Console.WriteLine("with List: result= "+item);
you are not using String.Format to insert arguments into the members of genlnkPLC, which are apparently intended as format strings. You can use
foreach (var item in genlnkPLC)
Console.WriteLine("without List result = " + String.Format(item, i_StationNum, 200));
instead.
The problem is that you can't change the reference of the elements enumerated in a foreach loop. string is an inmutable object, so changing it replaces the old reference with a new one. If you want to change the elements in the list, you'll need to do it in a for loop, like this:
for (int item = 0; item < genlnkPLC.Count; item++)
genlnkPLC[item]= String.Format(genlnkPLC[item], i_StationNum, 200);
No need to repeat foreach (the first one, did nothing to your item). Try this:
foreach (var item in genlnkPLC)
Console.WriteLine("with List: result= "+ String.Format(item, i_StationNum, 200));
As M.Bychenko says: "If you want to modify the existing list, you have to use for loop instead of foreach one:"
And thanks for the report tipp!
class Program
{
static void Main(string[] args)
{
int i_Num = 0;
string Str_Num = "";
string[] linkToPLC = {"toto[{0}].test{1}", "tata[{0}].test{1}", "titi[{0}].test{1}"};
List<string> genlnkPLCCanvas = new List<string>(linkToPLC);
List<string> genlnkPLCworkingwith = new List<string>(linkToPLC);
Console.WriteLine("Insert Num: ");
Str_Num = Console.ReadLine();
i_Num = Convert.ToInt32(Str_Num);
for (int item = 0; item < genlnkPLCCanvas.Count; item++)
{
genlnkPLCworkingwith[item] = String.Format(genlnkPLCCanvas[item], i_Num, 200);
}
var CanvasListReport = genlnkPLCCanvas.Select(item => "Canvas List = " + item);
var WorkingListReport = genlnkPLCworkingwith.Select(item => "Working list = " + item);//string.Format(item, i_Num, 200));
// now you can do whatever you want with the reports:
// - print them to console
// Console.WriteLine(string.Join(Envrironment.NewLine, withListReport));
// - save to file: File.WriteAllLines(#"C:\MyFile.txt", withListReport);
// - print to, say, WinForm UI:
// MyTextBox.Text = string.Join(Envrironment.NewLine, withListReport)
Console.WriteLine(string.Join(Environment.NewLine, CanvasListReport));
Console.WriteLine(string.Join(Environment.NewLine, WorkingListReport));
Console.ReadKey();
}
}
This is because
String.Format(item, i_StationNum, 200)
doesn't change the string in the list.
You have to assign the String.Format result to your item.

Three combination of dimension array in C#?

How do I create three combinations of dimension array in C#?, I am getting error message
index was outside of the bounds of the array.
foreach (XmlNode RegexExpression in XmlDataAccess.GetElementList(RefFile, "//regex"))
{
xRefList.Add(RegexExpression.InnerText);
}
foreach (XmlNode RegexExpression in XmlDataAccess.GetElementList(RefFile, "//word"))
{
WordList.Add(RegexExpression.InnerText);
}
foreach (XmlNode RegexExpression in XmlDataAccess.GetElementList(RefFile, "//title"))
{
TitleList.Add(RegexExpression.InnerText);
}
ArrayList xRefResult = MainDocumentPart_Framework.getReferenceContent(FileName, xRefList);
ArrayList TitleResult = MainDocumentPart_Framework.getReferenceContent(FileName, TitleList);
ArrayList WordResult = MainDocumentPart_Framework.getReferenceContent(FileName, WordList);
var FinalResult = from first in TitleResult.ToArray()
from second in WordList.ToArray()
from third in xRefResult.ToArray()
select new[] { first, second, third };
foreach (var Item in FinalResult)
{
System.Windows.MessageBox.Show(Item.ToString());
//I like to view show, all the combination of arrays
//first1, second1, third1
//first1, second1, third2
//first1, second1, third3 ...........
}
I'm not really sure what kind of output you're after, and I don't think you need to use LINQ for this.
string outputStr = "";
for(int x = 0;x<xRefList.Count;x++)
{
for(int y = 0;y<WordList.Count;y++)
{
for(int z = 0;z<TitleList.Count;z++)
{
outputStr += xRefList[x] + " " + WordList[y] + " " + TitleList[z] + "\n";
}
}
}
MessageBox.Show(outputStr);
Would something like this work?

Strange values in an array - after I change them they remain the same

Here is my code:
var items = tableInDatabase.All("WHERE [Order] > " + order);
foreach (var i in items.ToArray())
{
i.Order = 7;
}
tableInDatabase.Save(items.ToArray());
However, when the breakpoint comes to the last line (after the foreach loop), every element of items has an order the same as before (not 7). Why is this happening?
While still in the loop, i has an order of 7.
I'm using Massive, and this is the example from its official page:
var table = new Products();
var drinks = table.All("WHERE CategoryID = 8");
foreach(var item in drinks.ToArray()){
item.CategoryID = 12;
}
table.Save(drinks.ToArray());
I also tried:
foreach (var i in items.ToArray())
{
tableInDatabase.Update(i, i.Id);
}
And nothing.
The return type od tableInDatabase is the class TableInDatabase. This is the definition:
public TableInDatabase() : base(connectionString, "System.Data.SqlClient", "TableInDatabase", "Id") { }
use this code:
var items = tableInDatabase.All("WHERE [Order] > " + order);
var array = items.ToArray();
foreach (var i in array)
{
i.Order = 7;
}
tableInDatabase.Save(array);
ToArray() creates a new copy as said by Romil, In your case since you are using Massive, you can probably do what Adam has said, but that option creates two variable. You can try the following.
var items = tableInDatabase.All("WHERE [Order] > " + order).ToArray();
foreach (var i in items)
{
i.Order = 7;
}
tableInDatabase.Save(items);
This is a known issue with Massive example and has been reported here

Categories