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

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

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.

creating array of bad names to check and replace in c#

I'm looking to create a method that loops through an list and replaces with matched values with a new value. I have something working below but it really doesnt follow the DRY principal and looks ugly.
How could I create a dictionary of value pairs that would hold my data of values to match and replace?
var match = acreData.data;
foreach(var i in match)
{
if (i.county_name == "DE KALB")
{
i.county_name = "DEKALB";
}
if (i.county_name == "DU PAGE")
{
i.county_name = "DUPAGE";
}
}
In your question, you can try to use linq and Replace to make it.
var match = acreData.data.ToList();
match.ForEach(x =>
x.county_name = x.county_name.Replace(" ", "")
);
or you can try to create a mapper table to let your data mapper with your value. as #user2864740 say.
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("DE KALB", "DEKALB");
dict.Add("DU PAGE", "DUPAGE");
var match = acreData.data;
string val = string.Empty;
foreach (var i in match)
{
if (dict.TryGetValue(i.county_name, out val))
i.county_name = val;
}
If this were my problem and it is possible a county could have more than one common misspelling I would create a class to hold the correct name and the common misspellings. The you could easily determine if the misspelling exists and correct if. Something like this:
public class County
{
public string CountyName { get; set; }
public List<string> CommonMisspellings { get; set; }
public County()
{
CommonMisspellings = new List<string>();
}
}
Usage:
//most likely populate from db
var counties = new List<County>();
var dekalb = new County { CountyName = "DEKALB" };
dekalb.CommonMisspellings.Add("DE KALB");
dekalb.CommonMisspellings.Add("DE_KALB");
var test = "DE KALB";
if (counties.Any(c => c.CommonMisspellings.Contains(test)))
{
test = counties.First(c => c.CommonMisspellings.Contains(test)).CountyName;
}
If you are simply replacing all words in a list containing space without space, then can use below:
var newList = match.ConvertAll(word => word.Replace(" ", ""));
ConvertAll returns a new list.
Also, I suggest not to use variable names like i, j, k etc..but use temp etc.
Sample code below:
var oldList = new List<string> {"DE KALB", "DE PAGE"};
var newList = oldList.ConvertAll(word => word.Replace(" ", ""));
We can try removing all the characters but letters and apostroph (Cote d'Ivoire has it)
...
i.country_name = String.Concat(i.country_name
.Where(c => char.IsLetter(c) || c == '\''));
...
I made a comment under answer of #Kevin and it seems it needs further explanation. Sequential searching in list does not scale well and unfortunately for Kevin, that is not my opinion, asymptotic computational complexity is math. While searching in dictionary is more or less O(1), searching in list is O(n). To show a practical impact for solution with 100 countries with 100 misspellings each, lets make a test
public class Country
{
public string CountryName { get; set; }
public List<string> CommonMisspellings { get; set; }
public Country()
{
CommonMisspellings = new List<string>();
}
}
static void Main()
{
var counties = new List<Country>();
Dictionary<string, string> dict = new Dictionary<string, string>();
Random rnd = new Random();
List<string> allCountryNames = new List<string>();
List<string> allMissNames = new List<string>();
for (int state = 0; state < 100; ++state)
{
string countryName = state.ToString() + rnd.NextDouble();
allCountryNames.Add(countryName);
var country = new Country { CountryName = countryName };
counties.Add(country);
for (int miss = 0; miss < 100; ++miss)
{
string missname = countryName + miss;
allMissNames.Add(missname);
country.CommonMisspellings.Add(missname);
dict.Add(missname, countryName);
}
}
List<string> testNames = new List<string>();
for (int i = 0; i < 100000; ++i)
{
if (rnd.Next(20) == 1)
{
testNames.Add(allMissNames[rnd.Next(allMissNames.Count)]);
}
else
{
testNames.Add(allCountryNames[rnd.Next(allCountryNames.Count)]);
}
}
System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
st.Start();
List<string> repairs = new List<string>();
foreach (var test in testNames)
{
if (counties.Any(c => c.CommonMisspellings.Contains(test)))
{
repairs.Add(counties.First(c => c.CommonMisspellings.Contains(test)).CountryName);
}
}
st.Stop();
Console.WriteLine("List approach: " + st.ElapsedMilliseconds.ToString() + "ms");
st = new System.Diagnostics.Stopwatch();
st.Start();
List<string> repairsDict = new List<string>();
foreach (var test in testNames)
{
if (dict.TryGetValue(test, out var val))
{
repairsDict.Add(val);
}
}
st.Stop();
Console.WriteLine("Dict approach: " + st.ElapsedMilliseconds.ToString() + "ms");
Console.WriteLine("Repaired count: " + repairs.Count
+ ", check " + (repairs.SequenceEqual(repairsDict) ? "OK" : "ERROR"));
Console.ReadLine();
}
And the result is
List approach: 7264ms
Dict approach: 4ms
Repaired count: 4968, check OK
List approach is about 1800x slower, actually more the thousand times slower in this case. The results are as expected. If that is a problem is another question, it depends on concrete usage pattern in concrete application and is out of scope of this post.

Linq write new list from old list sublist, change said list, write back to old list

I'm rather new to Linq. I'm having trouble coding this.
I have a list with many different sublists.
oldList[0] some type
oldList[1] another different type
oldList[2] the type I want
oldList[3] more types
I want to select all the parameters from a specific type and write them to a temp list.
If that temp list is empty, I want to assign some values (values don't actually matter).
After changing the values, I want to write temp list back into oldList.
Please advise. This is a huge learning experience for me.
public void myFunction(list)
{
//list contains at least 5 sublists of various type
//check if the type I want is null
IEnumerable<TypeIWant> possiblyEmptyList = list.OfType<TypeIWant>(); //find the type I want from the list and save it
if (possiblyEmptyList == null) //this doesn't work and possiblyEmptyList.Count <= 1 doesn't work
{
//convert residence address to forwarding address
IEnumerable<ReplacementType> replacementList = list.OfType<ReplacementType>();
forwardingAddress = replacementList.Select(x => new TypeIWant /* this statement functions exactly the way I want it to */
{
Address1 = x.Address1,
Address2 = x.Address2,
AddressType = x.AddressType,
City = x.City,
CountryId = x.CountryId,
CountyRegion = x.CountyRegion,
Email = x.Email,
ConfirmEmail = x.ConfirmEmail,
Fax = x.Fax,
Telephone = x.Telephone,
State = x.State,
PostalCode = x.PostalCode
});
//write forwarding address back to list
//don't know how to do this
}
LINQ purpose is querying. It doesn't allow you to replace some items in collection with other items. Use simple loop instead:
IEnumerable<TypeIWant> possiblyEmptyList = list.OfType<TypeIWant>();
if (!possiblyEmptyList.Any())
{
for (int i = 0; i < list.Count; i++)
{
ReplacementType item = list[i] as ReplacementType;
if (item == null)
continue;
list[i] = ConvertToTypeIWant(item);
}
}
And conversion (which is better to do with something like automapper):
private TypeIWant ConvertToTypeIWant(ReplacementType x)
{
return new TypeIWant
{
Address1 = x.Address1,
Address2 = x.Address2,
AddressType = x.AddressType,
City = x.City,
CountryId = x.CountryId,
CountyRegion = x.CountyRegion,
Email = x.Email,
ConfirmEmail = x.ConfirmEmail,
Fax = x.Fax,
Telephone = x.Telephone,
State = x.State,
PostalCode = x.PostalCode
};
}
Not LINQ but an example.
class Program
{
static void Main(string[] args)
{
// Vars
var list = new List<List<string>>();
var a = new List<string>();
var b = new List<string>();
var c = new List<string> { "one", "two", "three" };
var d = new List<string>();
// Add Lists
list.Add(a);
list.Add(b);
list.Add(c);
list.Add(d);
// Loop through list
foreach (var x in list)
{
if (x.Count < 1)
{
var tempList = new List<string>();
tempList.Add("some value");
x.Clear();
x.AddRange(tempList);
}
}
// Print
int count = 0;
foreach (var l in list)
{
count++;
Console.Write("(List " + count + ") ");
foreach (var s in l)
{
Console.Write(s + " ");
}
Console.WriteLine("");
}
}
}
(List 1) some value
(List 2) some value
(List 3) one two three
(List 4) some value

ObservableCollection adds an item and overrides the others

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();

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