A single iterator of vectorS - c++

Is there an efficient way to have a single iterator iterate on the concatenation of 2 objects vector, as if they were one?
The two vectors contain the same data type of course.
UPDATE:
I think I should have put more details about my question and my context. This may answer some of the questions:
In fact I am having one attribute that store the last position of that iterator and inside one method I start to iterate from the last position where I stopped in the previous call, which might be in the first vector or in the second one.

What about this solution? It may be not elegant, but I guess it respects the standard. right?
vector<Whatever>::iterator it = vectorA.begin();
bool loopOnVectorA = true;
while(true) {
// My stuff here
if (loopOnVectorA && it == vectorA.end())
{
it = vectorB.begin();
loopOnVectorA = false;
}
else if (it == vectorB.end())
{
break;
}
else
{
varPtrIt++;
}
}

Related

Correct way to write only the first element of a c++ stl vector

I have a c++ code that at one part it stored some values of a measurement in a vector and this vector is a part of set of data schema which is serialized and then sent to a streamer.
There is new requirement that for a specific case I need just one value of the measurement which is always rewritten with the latest one, but I don't want to change the vector variable in order to keep the same schema. So I thought that for that case to rewrite each time the first element of the vector, something like this
vector<int> store_measurements;
int measurement = 10;
if (condition == "several_values")
{
store_measurements.pushback(measurement);
}
else
{
store_measurements.at(0) = measurement ;
}
It seems to work fine when the vector is not cleared, but I'd like to ask if this is the correct way to do that or there is a more preferable way to do it?
You can use the front() function.
vector<int> store_measurements;
int measurement = 10;
if (condition == "several_values")
{
store_measurements.push_back(measurement);
}
else
{
store_measurements.resize(1);
store_measurements.front() = measurement ;
}
Edit:
Based on the comments I added store_measurements.resize(1); before the assignment
I would probably use assign() which replaces all the values in the vector like this:
if (condition == "several_values")
{
store_measurements.push_back(measurement);
}
else
{
store_measurements.assign(1, measurement);
}

RemoveAt from StructArray Ue4

I struggle a bit with deleting struct from my TArray of structs.My struct contains AudioComponent and float.I was using Array.RemoveAt(index), but what i got from this was only removing half of my struct, which is AudioComponent.
Why is that? My function Removing elements looks like this:
void RemoveArrayElement( UAudioComponent AudioComponent )
{
for( int i=0; i<Array.Num(); i++ )
{
if( AudioComponent == Array[i].AudioComponent )
{
Array.RemoveAt( i );
}
}
}
What i want to achieve is completely deleting index, AudioComponent with it's float.
There are few issues with your code. As others mentioned in comments, you should use pointers. And if I'm not mistaken, you aren't allowed to use construction like this:
UPROPERTY()
TArray<UAudioComponent> invalidArray;
You should use UPROPERTY macro, otherwise your properties could and probably will be garbage collected. UPROPERTY wiki.
Next thing is that you are changing array over which you are iterating. I wrote few approaches, let's look at them:
void RemoveArrayElement(UAudioComponent* AudioComponent)
{
TArray<UAudioComponent*> audioArray; // array will be initialized somewhere else, this is for demo purpose.
// you always should check your pointers for validity
if (!AudioComponent || !AudioComponent->IsValidLowLevel() || AudioComponent->IsPendingKill())
return;
// Correct approach 1 (multiple):
TQueue<UAudioComponent*> toDelete;
for (int i = 0; i < audioArray.Num(); i++)
{
auto item = audioArray[i];
if (AudioComponent == item || true) // we simulate another condition for multiselect
{
toDelete.Enqueue(item);
}
}
// better approach for iteration:
for (auto item : audioArray)
if (item == AudioComponent || true) // we simulate another condition for multiselect
toDelete.Enqueue(item);
// finalize deletion in approach 1
UAudioComponent* deleteItem;
while (toDelete.Dequeue(deleteItem))
audioArray.Remove(deleteItem);
// almost correct approach 2 (single) :
UAudioComponent* foundItem;
for (auto item : audioArray)
if (item == AudioComponent)
{
foundItem = item;
break; // we can skip rest - but we must be sure, that items were added to collection using AddUnique(...)
}
if (foundItem)
audioArray.Remove(foundItem);
// correct and the best - approach 3 (single)
audioArray.Remove(AudioComponent);
}
First keep in mind that comparing two objects does not necessarily lead to the expected result of equality. Using the == operator means executing a function (bool operator==(L, R);) that specifies what should happen. So if you did not overload the == operator then you don't know what using it would result to unless you look at the source code where it's defined. Since you want to remove the exact audio component and not an instance of it that looks the same, you want to use pointers in your array. That also helps performance since your are not copying the whole component when calling RemoveArrayElement(...); but a single pointer. Also when there are two identical audio components stored in the array and they are at index a and a+1, then removing the audio component at index a the next iteration would skip your second audio component since all upper indexes are decremented by one.

Removing an object from vector while iterating through it

I'm new to C++ so I'm having trouble figuring out how to best remove an object from a vector while still iterating through it.
Basically, I need to iterate through two vectors. For every item, if the ID's match, I can remove them.
//For every person, check to see if the available bags match:
for(std::vector<Person>::iterator pit = waitingPeopleVector.begin(); pit != waitingPeopleVector.end(); ++pit) {
for(std::vector<Bag>::iterator bit = waitingBagsVector.begin(); bit != waitingBagsVector.end(); ++bit) {
int pId = pit->getId();
int bId = bit->getId();
if(pId == bId){
//a match occurs, remove the bag and person
}
}
}
Working with iterators is a bit confusing, I know I can use the .erase() function on my vectors, but I can't really pass pit or bit. Any help appreciated. Thanks
From the standard:
The iterator returned from a.erase(q) points to the element
immediately following q prior to the element being erased. If no such
element exists, a.end() is returned.
I would use it in something like using the erase method:
std::vector<Person>::iterator pit = waitingPeopleVector.begin();
std::vector<Bag>::iterator bit = waitingBagsVector.begin();
while (pit != waitingPeopleVector.end())
{
bool didit;
while (bit != waitingBagsVector.end())
{
didit = false;
if (pit->getId() == bit->getId() && !didit)
{
bit = waitingBagsVector.erase(bit);
pit = waitingPeopleVector.erase(pit);
didit = true;
}
else
{
++bit;
}
}
if (didit)
continue;
else
++pit;
}
Using the erase-remove idiom will achieve this objective, the below offers an (untested) way using lambdas and <algorithm> functions to remove elements from wPL which have the same ID as wBL. It shouldn't be too much effort to extend this to both lists. Note, we have used std::list instead of std::vector for faster removal.
std::list<Person> wPL;
std::list<Bag> wBL;
//...
wPL.erase(std::remove_if(wPL.begin(), wPL.end(),
[&wBL](auto x) { return std::find_if(wBL.begin(), wBL.end(), [](auto y)
{ return x.getId() == y.getId();); }), wPL.end() };

Vector iterators incompatible... but why?

I receive the message "Vector iterators incompatible". I tried to wrap my head around it, but nothing. I did it before. Same code, just not used in a class that receives "cWORLD* World". What am I doing wrong?
Thank you!
else if (Click[2] == true)
{
//go through objects and check collision
for (vector<cOBJECT*>::iterator it = World->ReturnWorldObjects().begin(); it != World->ReturnWorldObjects().end();)
{
//Check for collision and delete object
if (PointInRect(MouseX + offX, MouseY + offY, (*it)->getrect()) == true)
{
// delete object, delete slot, pick up next slot
delete *it;
it = World->ReturnWorldObjects().erase(it);
}
else
{ // no action, move to next
++it;
}
}//for
}//else if (Click[2] == true)
Looks like ReturnWorldObjects returns copy of vector, not reference. In this case, you are trying to compare iterators of different objects, that is not checked by standard, but can be checked by checked iterators (in this case, I think it's MSVC checked iterators).
Like #ForEveR already mentioned, you possibly return a copy of a vector in the function ReturnWorldObjects(). Without seeing the declaration of this method I can only assume it's something like vector<cOBJECT*> ReturnWorldObject();
You can come around this with 2 Solutions, I think:
1. Return a reference to the vector in your World Class
const vector<cOBJECT*>& ReturnWorldObjects()
{
return m_vecWorldObjects; // Your vector here
}
2. Get one copy of that function and use that in your code
...
vector<cOBJECT*> worldObjects = World->ReturnWorldObjects();
for (vector<cOBJECT*>::iterator it = worldObjects.begin(); it != worldObjects.end(); it++)
{
...
}
...

What is proper way to delete objects that resides in a list that you find while looping that list?

I have a list of Star structs. These structs are in a std::list
I am double looping this list and compairing there locations to detect a collision. When A collision is found I will delete Star with the lowest mass. But how can I delete the Star when I am in the double Loop, and keep the loop going to check for more collisions?
It's worth mentioning that the second loop is a reverse loop.
Here is some code
void UniverseManager::CheckCollisions()
{
std::list<Star>::iterator iStar1;
std::list<Star>::reverse_iterator iStar2;
bool totalbreak = false;
for (iStar1 = mStars.begin(); iStar1 != mStars.end(); iStar1++)
{
for (iStar2 = mStars.rbegin(); iStar2 != mStars.rend(); iStar2++)
{
if (*iStar1 == *iStar2)
break;
Star &star1 = *iStar1;
Star &star2 = *iStar2;
if (CalculateDistance(star1.mLocation, star2.mLocation) < 10)
{
// collision
// get heaviest star
if (star1.mMass > star2.mMass)
{
star1.mMass += star2.mMass;
// I need to delete the star2 and keep looping;
}
else
{
star2.mMass += star1.mMass;
// I need to delete the star1 and keep looping;
}
}
}
}
}
You need to utilize the return value of the erase method like so.
iStar1 = mStars.erase(iStar1);
erase = true;
if (iStar1 == mStars.end())
break; //or handle the end condition
//continue to bottom of loop
if (!erase)
iStar1++; //you will need to move the incrementation of the iterator out of the loop declaration, because you need to make it not increment when an element is erased.
if you don't increment the iterator if an item is erased and check if you deleted the last element then you should be fine.
Since modifying the list invalidates the iterators (so that you cannot increment them), you have to keep safe the iterators before the list is changed.
In the most of the implementation std::list is a dual-linked list, hence a iteration like
for(auto i=list.begin(), ii; i!=list.end(); i=ii)
{
ii = i; ++ii; //ii now is next-of-i
// do stuff with i
// call list.erasee(i).
// i is now invalid, but ii is already the "next of i"
}
The safest way, is to create a list containing all the "collided", then iterate on the "collided" calling list.remove(*iterator_on_collided)
(but inefficient, since has O2 complexity)
You want to use the result of erase() to get the next iterator and advance the loop differently:
If you erase using the outer iterator you clearly can abondon checking this Star against others and break out of the inner loop. Only if the inner loop was complete you'd want to advance the outer iterator because otherwise it would be advanced by the erase().
If you erase using the inner loop you already advanced the iteration, otherwise, i.e. if no star was erased, you need to advance.
Sample code would look somethimg like this:
for (auto oit(s.begin()), end(s.end()); oit != end; )
{
auto iit(s.begin());
while (iit != end)
{
if (need_to_delete_outer)
{
oit = s.erase(oit);
break;
}
else if (need_to_delete_inner)
{
iit = s.erase(iit);
}
else
{
++iit;
}
}
if (iit == end)
{
++oit;
}
}