473,396 Members | 1,860 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Style Question: erasing map element in for loop

There's the thing about iterating though a map or vector when you may delete
one of the elements, where you simply assign the iterator to the map.erase()
statement or increment it if you don't. Well, I have this issue where I may
delete the map element in 3 different places:
for ( map_key_pcmissile::iterator mit = World.Missiles.begin(); mit !=
World.Missiles.end(); )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
else
{
if ( /* Some Condition */ )
{
for ( /* Iterate through vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
for ( /* Iterate through a different vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
}
++mit;
}
}

As you can see two of these are fairly heavily nested and so the ++mit; at
the end shouldn't occur if the map elements were deleted in any of the
above. Right now it's just checking one.

I'm wondering the best way to do this. The first obvious way is to simply
set some boolean flag if the element was deleted and only increment mit if
the flag is set to false. This is my usual solution in this type of
problem, but I don't tend to like it. It just seems not the Right Thing.
What would you do in this case? What is the Right Thing to do?
May 29 '06 #1
8 2118
Jim Langston wrote:
There's the thing about iterating though a map or vector when you may delete
one of the elements, where you simply assign the iterator to the map.erase()
statement or increment it if you don't. Well, I have this issue where I may
delete the map element in 3 different places:
for ( map_key_pcmissile::iterator mit = World.Missiles.begin(); mit !=
World.Missiles.end(); )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
else
{
if ( /* Some Condition */ )
{
for ( /* Iterate through vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
for ( /* Iterate through a different vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
}
++mit;
}
}

As you can see two of these are fairly heavily nested and so the ++mit; at
the end shouldn't occur if the map elements were deleted in any of the
above. Right now it's just checking one.

I'm wondering the best way to do this. The first obvious way is to simply
set some boolean flag if the element was deleted and only increment mit if
the flag is set to false. This is my usual solution in this type of
problem, but I don't tend to like it. It just seems not the Right Thing.
What would you do in this case? What is the Right Thing to do?


What was std::remove_if() designed for? With little detail you gave us
that is the only advice i can give.

Ben
May 29 '06 #2
* Jim Langston:
There's the thing about iterating though a map or vector when you may delete
one of the elements, where you simply assign the iterator to the map.erase()
statement or increment it if you don't. Well, I have this issue where I may
delete the map element in 3 different places:
for ( map_key_pcmissile::iterator mit = World.Missiles.begin(); mit !=
World.Missiles.end(); )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
else
{
if ( /* Some Condition */ )
{
for ( /* Iterate through vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
for ( /* Iterate through a different vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
}
++mit;
}
}
Unless the code checks for World.Missiles.end() in each of the last
cases, this code risk deleting its way right out of the map: there can
potentially be multiple deletes in each iteration of the outer loop.

As you can see two of these are fairly heavily nested and so the ++mit; at
the end shouldn't occur if the map elements were deleted in any of the
above. Right now it's just checking one.

I'm wondering the best way to do this. The first obvious way is to simply
set some boolean flag if the element was deleted and only increment mit if
the flag is set to false. This is my usual solution in this type of
problem, but I don't tend to like it. It just seems not the Right Thing.
What would you do in this case? What is the Right Thing to do?


A boolean flag is IMO the way to go if more than one delete can occur in
each outer loop iteration.

Otherwise there is the keyword 'continue'.

There's also 'return', like, for the single delete per iteration case,

bool eraseGoner(
map_key_pcmissile& map, map_key_pcmissile::iterator& it
)
{
// lots of stuff here, including
i = map.erase( i );
return true;
//
return false;
}
map_key_pcmissile::iterator mit = World.Missiles.begin();
while( mit != World.Missiles.end(); )
{
if( !eraseGoner( World.Missiles, mit ) ) { ++mit; }
}
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 29 '06 #3

"benben" <be******@yahoo.com.au> wrote in message
news:44***********************@news.optusnet.com.a u...
Jim Langston wrote:
There's the thing about iterating though a map or vector when you may
delete one of the elements, where you simply assign the iterator to the
map.erase() statement or increment it if you don't. Well, I have this
issue where I may delete the map element in 3 different places:
for ( map_key_pcmissile::iterator mit = World.Missiles.begin(); mit !=
World.Missiles.end(); )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
else
{
if ( /* Some Condition */ )
{
for ( /* Iterate through vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
for ( /* Iterate through a different vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
}
++mit;
}
}

As you can see two of these are fairly heavily nested and so the ++mit;
at the end shouldn't occur if the map elements were deleted in any of the
above. Right now it's just checking one.

I'm wondering the best way to do this. The first obvious way is to
simply set some boolean flag if the element was deleted and only
increment mit if the flag is set to false. This is my usual solution in
this type of problem, but I don't tend to like it. It just seems not the
Right Thing. What would you do in this case? What is the Right Thing to
do?


What was std::remove_if() designed for? With little detail you gave us
that is the only advice i can give.


std::remove_if() won't work because I need there are other situations where
I need to not delete an element, and also I have to do stuff with the
element before I delete it.
May 29 '06 #4

"Alf P. Steinbach" <al***@start.no> wrote in message
news:4e*************@individual.net...
* Jim Langston:
There's the thing about iterating though a map or vector when you may
delete one of the elements, where you simply assign the iterator to the
map.erase() statement or increment it if you don't. Well, I have this
issue where I may delete the map element in 3 different places:
for ( map_key_pcmissile::iterator mit = World.Missiles.begin(); mit !=
World.Missiles.end(); )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
else
{
if ( /* Some Condition */ )
{
for ( /* Iterate through vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
for ( /* Iterate through a different vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
}
++mit;
}
}
Unless the code checks for World.Missiles.end() in each of the last cases,
this code risk deleting its way right out of the map: there can
potentially be multiple deletes in each iteration of the outer loop.

As you can see two of these are fairly heavily nested and so the ++mit;
at the end shouldn't occur if the map elements were deleted in any of the
above. Right now it's just checking one.

I'm wondering the best way to do this. The first obvious way is to
simply set some boolean flag if the element was deleted and only
increment mit if the flag is set to false. This is my usual solution in
this type of problem, but I don't tend to like it. It just seems not the
Right Thing. What would you do in this case? What is the Right Thing to
do?


A boolean flag is IMO the way to go if more than one delete can occur in
each outer loop iteration.


Actually, that is something I didn't state. If the element is deleted, it
needs to stop processing that element and go to the next iteration of the
out enclosing for loop. Of course the easiest way would be to have a goto
statement, but I just don't use goto's, even though some may argue this is a
case to use it, I still don't like it.
Otherwise there is the keyword 'continue'.
Yes, although continue only exits the smallest enclosing loop. I think I'll
need to set a flag and use continue twice.
There's also 'return', like, for the single delete per iteration case,

bool eraseGoner(
map_key_pcmissile& map, map_key_pcmissile::iterator& it
)
{
// lots of stuff here, including
i = map.erase( i );
return true;
//
return false;
}
map_key_pcmissile::iterator mit = World.Missiles.begin();
while( mit != World.Missiles.end(); )
{
if( !eraseGoner( World.Missiles, mit ) ) { ++mit; }
}


Yeah, I've considered how breaking this up into functions may help me, and
that may be what I'll need to do to make it easiest.

What this is actually for is a game server. The outer iterator is iterating
thorough beams (bullets if you will). The first if statement sees if the
bullet has gone out of range, if it has it deletes it. The first enclosed
for statement is iterating through the players. If the beam hits a player
then the server makes the player take damage, sends messages, etc... then
deletes the beam. The second iterator interates through NPCs doing the same
thing. Which is why the map element (the bullet) may be deleted in one of
three places.
May 29 '06 #5
If you don't like to use goto, use return...

outer:
while(foo)
while(bar)
if(answer == 42) goto outer;

becomes;

void makeReturnLikeContinueOuter()
{
while(foo)
while(bar)
if(answer == 42) return;
}

while(foo)
makeReturnLikeContinueOuter();
This is the only case so far I found the use of goto appropriate.

May 29 '06 #6
Jim Langston wrote:
for ( map_key_pcmissile::iterator mit = World.Missiles.begin(); mit !=
World.Missiles.end(); )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
else
{
if ( /* Some Condition */ )
{
for ( /* Iterate through vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
for ( /* Iterate through a different vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
}
++mit;
}
}


Since is seems like you basically want to select a single action, which
is either "increment the iterator" or "do stuff and delete," why not
have a variable to represent that action? That variable will have only
one value, making the whole question of performing two incompatible
actions moot.

How about something like:

for (MapIterator mit = map.begin(), end = map.end(); mit != end; ++mit)
{
// Use a callable type which provides a hook for the
just-prior-to-delete behavior.
typedef SomeCallableType ElementFunctor;

// Choose one action to take -- either increment, or perform some
logic and delete.
ElementFunctor action = Increment;
if (cond1) action = new DeletionActionA;
else if (cond2) action = new DeletionActionB;

// After conditional logic,
action(mit);
}

Modify as necessary to suit your needs. For example, if you need to do
two deletion actions, but only actually delete once (obviously you
don't want to delete twice), you could use a chaining mechanism or some
such to combine the two actions. I'm sure you can sort out the
details.

Luke

May 29 '06 #7

"Jim Langston" <ta*******@rocketmail.com> wrote in message
news:%g**************@fe02.lga...
There's the thing about iterating though a map or vector when you may
delete one of the elements, where you simply assign the iterator to the
map.erase() statement or increment it if you don't. Well, I have this
issue where I may delete the map element in 3 different places:
for ( map_key_pcmissile::iterator mit = World.Missiles.begin(); mit !=
World.Missiles.end(); )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
else
{
if ( /* Some Condition */ )
{
for ( /* Iterate through vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
for ( /* Iterate through a different vector */ )
{
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/*** Do some stuff and delete map element here ***/
}
}
}
}
++mit;
}
}

As you can see two of these are fairly heavily nested and so the ++mit; at
the end shouldn't occur if the map elements were deleted in any of the
above. Right now it's just checking one.

I'm wondering the best way to do this. The first obvious way is to simply
set some boolean flag if the element was deleted and only increment mit if
the flag is set to false. This is my usual solution in this type of
problem, but I don't tend to like it. It just seems not the Right Thing.
What would you do in this case? What is the Right Thing to do?


What I finally settled on:

bool CheckIfPlayerHit( map_key_pcmissile::iterator& mit )
{
for ( /* Iterate though map */)
{
if ( /* Some Condition */ ) {
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/* do stuff */
World.Missiles.erase(mit);
return true;
}
}
}
return false;

}

bool CheckIfNPCHit( map_key_pcmissile::iterator& mit )
{
for ( /* Iterate though map */)
{
if ( /* Some Condition */ ) {
if ( /* Some Condition */ )
{
if ( /* Some Condition */ )
{
/* do stuff */
World.Missiles.erase(mit);
return true;
}
}
}
return false;
}

/* in main function (not main()) */
for ( map_key_pcmissile::iterator mit = World.Missiles.begin(); mit !=
World.Missiles.end(); )
{
if ( /* Some Condition */ )
{
mit = World.Missiles.erase(mit);
}
else
{
if ( /* Some Condition */ ) {
{
if ( CheckIfPlayerHit( mit ) )
continue;

if ( CheckIfNPCHit( mit ) )
continue;
}
++mit;
}
}

This does have the advantage of making my main function much clearer. I
think this is probably the best solution.


May 30 '06 #8
Jim Langston wrote:
There's the thing about iterating though a map or vector when you may delete
one of the elements, where you simply assign the iterator to the map.erase()
statement or increment it if you don't. Well, I have this issue where I may
delete the map element in 3 different places:


[code snipped]

As you can see two of these are fairly heavily nested and so the ++mit; at
the end shouldn't occur if the map elements were deleted in any of the
above. Right now it's just checking one.

I'm wondering the best way to do this. The first obvious way is to simply
set some boolean flag if the element was deleted and only increment mit if
the flag is set to false. This is my usual solution in this type of
problem, but I don't tend to like it. It just seems not the Right Thing.
What would you do in this case? What is the Right Thing to do?

How about...

typedef map_key_pcmissile::iterator mkpIt;

for (mkpIt mit = World.Missiles.begin(), nextIt = mit;
mit != World.Missiles.end();
mit = nextIt);
{
++(nextIt = mit);

// do stuff; don't worry about advancing mit
}

You could also integrate the ++(nextIt = mit) into the continuation
condition if you wanted.

-Mark
May 30 '06 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
by: Generic Usenet Account | last post by:
To settle the dispute regarding what happens when an "erase" method is invoked on an STL container (i.e. whether the element is merely removed from the container or whether it also gets deleted in...
10
by: Pat | last post by:
Hi, I use "vector<int> v" in my program. If "v" contains the following: (front) 1 2 3 4 2 5 7 1 (end), I want to remove some element, say "2", and I want the resultant "v" to be as follows:...
15
by: lawrence | last post by:
Sorry for the dumb question but I'm new to Javascript. I wrote this script hoping to animate some div blocks on a page. You can see the page here: http://www.keymedia.biz/demo.htm Can anyone...
0
by: Xah Lee | last post by:
One-Liner Loop in Functional Style Xah Lee, 200510 Today we show a example of a loop done as a one-liner of Functional Programing style. Suppose you have a list of file full paths of...
11
by: eeykay | last post by:
Hello, I am facing a starnge problem while erasing the last member in a vector. I am using VC++ .NET 2002 complier. I have vector of CComPtr<..> (irrelevant here), and then I iterate over the...
5
by: ma740988 | last post by:
For starters, Happy New Year to all!! I created a vector of pairs where pair first is a primitive and pair second is a vector of ints. So now: # include <iostream> # include <vector>
13
by: mahajan.vibhor | last post by:
I have a list of pointers. e.g A* a = new A(); // A is a class stl::list<A*list_a; I am inserting object of class in the after allocating memeory thru new operator. But when i want to...
3
by: =?iso-8859-1?q?Erik_Wikstr=F6m?= | last post by:
I have some code where there's this vector of pointers to objects and I need to delete and erase some of them, the problem is that to know which I need to iterate through the vector and I'm trying...
3
by: Angus | last post by:
If I want to erase all list items with a value of say 3 as below: std::list<intmylist; mylist.push_back(3); mylist.push_back(4); mylist.push_back(5); mylist.push_back(6); ...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.