I have this code that removes a player if the player is not alive, but I figured the problem is to do with the foreach loop. I've seen solutions involving making new lists, but I cannot see how I can apply it to my code. Can anyone please shed some light?
private Dictionary<int, Player> numPlayers = new Dictionary<int, Player>();
private void CheckPlayers()
{
foreach (Player player in numPlayers.Values)
{
if (!player.isAlive)
{
canvas.Children.Remove(player.hand);
numPlayers.Remove(player.id); // breaks here
}
}
}
Query the collection for the players to delete:
var playersToDelete = numPlayers.Values.Where(p => !p.isAlive).ToList();
Then delete the players:
foreach(var player in playersToDelete) {
canvas.Children.Remove(player.hand);
numPlayers.Remove(player.id);
}
You can't modify the collection you are iterating over with foreach. What you need to do is add the items you want to remove to a new list, then once that list is built remove them.
var dead = numPlayers.Values
.Where(p => !p.isAlive)
.ToList();
foreach(var player in dead)
{
canvas.Children.Remove(player.hand);
numPlayer.Remove(player.id);
}
You should remove the elements of a collection using a reverse for:
for(int i = numPlayers.Values.Count - 1; i <= 0; i--)
{
//Remove it.
}
Related
Im reading from xml file using foreach (as in below) and writing found info into a List, which then is later added to a list of lists. My problem is that the moment foreach loop is trying to add another element to my lists of lists it somehow erases the content of previous elements of the list and instead adds x of the same. E.g. first loop is ok, on the second loop it erases the first element and adds 2 of the same, on the 3rd loop it adds 3 same lists etc.
It might be a simple problem but i really cannot think of a solution to at the moment.
Code:
static List<List<string>> AddPapers(XmlNodeList nodelist)
{
var papers = new List<List<string>>();
var paper = new List<string>();
foreach (XmlNode node in nodelist)
{
paper.Clear();
for (int i = 0; i < node.ChildNodes.Count; i++)
{
paper.Add(node.ChildNodes[i].InnerText);
}
papers.Add(paper);
}
return papers;
}
More info: This is sort of a simplified version without all the fancy stuff id do with the xml but nevertheless, the problem is the same.
The paper list is good everytime i check so the problem should be with adding to papers. I honestly have no idea why or even how can it erase the contents of papers and add same values on its own.
The problem is that you're only calling paper.Clear, which clears the list that you just added, but then you re-populate it with new items and add it again.
Instead, you should create a new instance of the list on each iteration, so you're not always modifying the same list over and over again (remember a List<T> is a reference type, so you're only adding a reference to the list).
For example:
static List<List<string>> AddPapers(XmlNodeList nodelist)
{
var papers = new List<List<string>>();
foreach (XmlNode node in nodelist)
{
// Create a new list on each iteration
var paper = new List<string>();
for (int i = 0; i < node.ChildNodes.Count; i++)
{
paper.Add(node.ChildNodes[i].InnerText);
}
papers.Add(paper);
}
return papers;
}
Also, using System.Linq extention methods, your code can be reduced to:
static List<List<string>> GetChildrenInnerTexts(XmlNodeList nodes)
{
return nodes.Cast<XmlNode>()
.Select(node => node.ChildNodes.Cast<XmlNode>()
.Select(child => child.InnerText)
.ToList())
.ToList();
}
The issue is with reference. You need to initialize 'paper' instead of clearing it.
Inside you first foreach loop, change
paper.Clear()
With
paper = new List<string>();
When you clear the object, you are keeping the reference to empty object for every index of papers
I'm making a program that has 2 dictionaries
Queue and Playing
And I want to grab the first element from Queue and move to Playing when Playing is empty
The only code piece I have is
if(Playing.Count == 0){}
Any way I can do that?
PS: Both Playing and queue require an int and a player, the player is defined, the int is the player ID
EDIT
Code example:
public static Dictionary<int, player> queue = new Dictionary<int, player>();
public static Dictionary<int, player> Playing = new Dictionary<int,
player>();
if (Regex.IsMatch(m.GetString(1).Substring(0, 1), #"\.|\!|\?|\-|\+"))
{
var cmdprefix = m.GetString(1).Substring(0, 1); //Set the command prefix
var words = m.GetString(1).ToLower().Split(' '); //Split the spacebar into words.
if (words[0].StartsWith(cmdprefix + "queue")); //If words starts with the prefix and "queue"
{
queue.Add(m.GetInt(0), new player()
{
username = m.GetString(1), //Nickname of player
});
}
if(Playing.Count == 0)
{
}
}
I believe I have answered your question with below code. The trick is that an item is removed from a Dictionary by its key.
if(Playing.Count == 0)
{
Playing.Add(
m.GetInt(0),
new player{ username = m.GetString(1)
}
queue.Remove(m.GetInt(0);
}
Also note the comments from #paparazzo and #Eser that Dictionary is the wrong choice. Queue, SortedList or SortedDictionary should be better.
I have a list. When this list populate, I've got somethings like that:
List<T> {Soc1, Soc2, Soc3}
with Soc complex object. Then I modify the list: delete Soc2, add Soc4:
List<T> {Soc1, Soc3, Soc4}
Now, on db, I've got the first list (1,2,3) and I must update with the new one (1,3,4). How to do this check in c#? I try to use the List method contains
foreach(T t in oldList){
if(t.Contains(oldList)){
...}
for add the new item(s), but I stop on the remove of element that not exist anymore (Soc2 in this example). How to do? Thanks
foreach will break when you modify the list (delete an item). Therefore it's better to use while instead.
You use a while for the old list to remove the elements which don't exist anymore and afterwards a foreach trough the new list to add the new items.
List<T> oldList = new List<T> { Soc1, Soc2, Soc3 };
List<T> newList = new List<T> { Soc1, Soc3, Soc4 };
int i = 0;
// Go trough the old list to remove items which don't exist anymore
while(i < oldList.Count)
{
// If the new list doesn't contain the old element, remove it from the old list
if (!newList.Contains(oldList[i]))
{
oldList.RemoveAt(i);
}
// Otherwise move on
else
{
i++;
}
}
// Now go trough the new list and add all elements to the old list which are new
foreach(T k in newList)
{
if (!oldList.Contains(k))
{
oldList.Add(k);
}
}
You could do two loops and use LINQ:
// add the new ones...
foreach (var newItem in newList.Where(n => !oldList.Any(o => o.Id == n.Id))) {
oldList.Add(newItem);
}
// remove the redundant ones...
var oldIds = oldList.Select(i => i.Id);
foreach (var oldId in oldIds) {
if (!newList.Any(i => i.Id == oldId)) {
oldList.Remove(oldList.First(i => i.Id == oldId));
}
}
Collection was modified; enumeration operation may not execute.
I get that error when I run my program.
I have a dictionary and when I modify it I can't enumerate from it.
public void Render() {
foreach (var pair in players ) {
Main.SpriteBatch.DrawString(Main.BigFont, pair.Key, pair.Value, Color.White);
}
}
I tried modifying it at the value or removing it completely, then it must re-add it.
byte[] data = client.EndReceive(ar, ref ip);
string str_data = Encoding.ASCII.GetString(data);
string[] coords = str_data.Split(";".ToCharArray());
if (players.ContainsKey(coords[0])) {
players.Remove(coords[0]);
}
players.Add(coords[0], new Vector2(Int32.Parse(coords[1]), Int32.Parse(coords[2])));
You can also try to use an ordinary for loop instead of a foreach.
That way you can circumvent the warning, since you're not using the enumeration option.
For a dictionary you would do:
List<string/*or other datatype*/> keys = players.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{
Main.SpriteBatch.DrawString(Main.BigFont, keys[i], players[keys[i]], Color.White);
}
Does this suit your needs?
var ToBeDeleted = new List<player>(); //Or whatever type was the list players holding
if (players.ContainsKey(coords[0])) {
ToBeDeleted.Add(coords[0]);
}
foreach(var item in ToBeDeleted)
{
players.Remove(item);
}
You cannot modify (add, update or remove) any collection while you are iterating over it. It is restricted in c#.
You need to modify your code so that you keep any updation on the list seperate. You can keep a seperate record for any changes that you have made during the loop and merge after the loop.e.g
var itemsToDelete = new Dictionary<int,Player>();
then change your this line
if (players.ContainsKey(coords[0])) {
players.Remove(coords[0]);
}
to
if (players.ContainsKey(coords[0])) {
itemsToDelete.Add(coords[0]);
}
then after your loop
foreach (var key in itemsToDelete)
{
players.Remove(key);
}
Here is my code:
foreach (OrderItem item in OrderInfo.order)
{
orderItemViews.Single(i => i.numericUpDown.Name == item.id.ToString()).numericUpDown.Value = item.count;
}
It gives an exception.
I know that I can't change the collection inside foreach
How can I change this code to make it work? Best of all if it would be LINQ code.
exception says that "The collection was modified". sorry can't provide real message of exception because it in non-english
sorry guys. I've found where collection is changing. It was inside *numericUpDown_ValueChanged* handler. anyway I've got an answer. thank you
You can use ToList(), Like this :
foreach (OrderItem item in OrderInfo.order.ToList())
{
orderItemViews.Single(i => i.numericUpDown.Name == item.id.ToString()).numericUpDown.Value = item.count;
}
Or use normal for loop :
for (int i = 0 ; i < OrderInfo.order.Count; i++)
{
OrderItem item = OrderInfo.order[i];
orderItemViews.Single(i => i.numericUpDown.Name == item.id.ToString()).numericUpDown.Value = item.count;
}
Tip : Performance wise, It's better to use the second way.
This is what I do, when I need to modify the collection.
foreach (OrderItem item in OrderInfo.order.ToList())
{
...
}
Create a copy. Enumerate the copy, but update the original one.
You can use an extension ToEach static method:
public delegate void ToEachTransformAction<T>(ref T Telement);
[Extension()]
public static void ToEach<T>(Generic.IList<T> thislist, ToEachTransformAction<T> Transform)
{
for (var n = 0; n < thislist.Count; n++)
{
Transform.Invoke(thislist(n));
}
}