So I have a vector of unsigned ints (vector<unsigned int> is called vector1). I have another vector of a struct I created (vector<struct> is called vector2). vector<int> holds an integer that is the index of the vector<struct>. For example, let's say that vector<int = {5, 17, 18, 19}. That means vector2.at(5) == vector2.at(vector1.at(0)).
In the struct, I have a bool variable called var. In most cases, var is false. I want to delete all of the elements in vector1 that have var = true.
What I did was:
for (unsigned int i = 0; i < vector1.size(); i++)
{
if (vector2.at(vector1.at(i)).var)
vector1.erase(vector.begin() + i);
}
The only problem with this is that it does not delete all of the true elements. I have run the for loop multiple times for all values to be delete. Is this the correct behavior? If it is not, where did I go wrong?
You have to use the erase-remove idiom to delete elements from a vector.
v.erase(std::remove(v.begin(), v.end(), value), v.begin);
std::remove moves the elements to the end of the vector and erase will erase the element from the vector.
You can keep a temporary vector, copy of vector1 and iterate over it in the for loop and delete from vector1.
You are erasing elements in the vector while at the same time iterating over it. So when erasing an element, you always jump over the next element, since you increase i while having just shortened the vector at i (it would be even worse, had you used a proper iterator loop instead of an index loop). The best way to do this would be to seperate both opretions, first "marking" (or rather reordering) the elements for removal and then erasing them from the vector.
This is in practice best done using the erase-remove idiom (vector.erarse(std::remove(...), vector.end())), which first uses std::remove(_if) to reorganize the data with the non-removed elements at the beginning and returns the new end of the range, which can then be used to really delete those removed elements from the range (effectively just shortening the whole vector), using std::vector::erase. Using a C++11 lambda, the removal condition can be expressed quite easily:
vector1.erase(std::remove_if( //erase range starting here
vector1.begin(), vector1.end(), //iterate over whole vector
[&vector2](unsigned int i) //call this for each element
{ return vector2.at(i).var; }), //return true to remove
vector1.end()); //erase up to old end
EDIT: And by the way, as always be sure if you really need std::vector::at instead of just [] and keep in mind the implications of both (in particular the overhead of the former and "maybe insecurity" of the latter).
Related
I am trying to do a double loop across a std::vector to explore all combinations of items in the vector. If the result is good, I add it to the vector for another pass. This is being used for an association rule problem but I made a smaller demonstration for this question. It seems as though when I push_back it will sometimes change the vector such that the original iterator no longer works. For example:
std::vector<int> nums{1,2,3,4,5};
auto nextStart = nums.begin();
while (nextStart != nums.end()){
auto currentStart = nextStart;
auto currentEnd = nums.end();
nextStart = currentEnd;
for (auto a = currentStart; a!= currentEnd-1; a++){
for (auto b = currentStart+1; b != currentEnd; b++){
auto sum = (*a) + (*b);
if (sum < 10) nums.push_back(sum);
}
}
}
On some iterations, currentStart points to a location that is outside the array and provides garbage data. What is causing this and what is the best way to avoid this situation? I know that modifying something you iterate over is an invitation for trouble...
nums.push_back(sum);
push_back invalidates all existing iterators to the vector if push_back ends up reallocating the vector.
That's just how the vector works. Initially some additional space gets reserved for the vector's growth. Vector's internal buffer that holds its contents has some extra room to spare, but when it is full the next call to push_back allocates a bigger buffer to the vector's contents, moves the contents of the existing buffer, then deletes the existing buffer.
The shown code creates and uses iterators for the vector, but any call to push_back will invalidate the whole lot, and the next invalidated vector dereference results in undefined behavior.
You have two basic options:
Replace the vector with some other container that does not invalidate its existing iterators, when additional values get added to the iterator
Reimplement the entire logic using vector indexes instead of iterators.
int main() {
std::vector<std::vector<int>> v;
v.push_back({1,2,3,4});
auto it = v.at(0).begin();
int size = v.at(0).size();
std::cout<<size<<std::endl;
for (int i = 0; i < size; ++it)
{
v.push_back({5,6,7,8});
//std::cout<<*it<<std::endl;
}
return 0;
}
The iterator is broken when I push some elements into the outer container.
what should I do if I really want to iterate the container element inside of outer container and at the same time keep pushing back some new elements ? Many thanks!
When the outer vector resizes, it must do one of two things:
copy the elements
move the elements
It can only move the elements if the type has nonthrowing move semantics (the move constructor is marked noexcept, etc).
In this case, the element is a vector holding integers, which recursively depends on its elements. Since integer does not throw, the inner vector should be noexcept-movable too. The standard requires the iterators to remain valid when moving such a vector.
You say iterator is broken in your example, but the real problem is a bug in your code:
for (int i = 0; i < size; ++it) // << HERE
{
v.push_back({5,6,7,8});
//std::cout<<*it<<std::endl;
}
You don't increment the loop variable, you increment the iterator, in an infinite loop
what should I do if I really want to iterate the container element inside of outer container and at the same time keep pushing back some new elements ?
I see 3 possible solutions:
use container that does not relocate elements when new ones are added, for example std::map<size_t,std::vector<int>>, you should be aware of different memory usage and access speed
use index of inner array instead of iterator
add data to a temporary vector in the loop and after you done append all data to the original vector
I am inserting elements with push_back in a vector. I want to read the data in FIFO and using iterator assigned to begin of vector. Is there any other approach of reading data in FIFO in a vector?
You may use a std::deque() and its pop_front() method.
You can access the elements of a vecotr just like you would access the elements of an array:
std::vector<std::string> vec;
// Excluded: push items onto vec
for (int i = 0; i < vec.size(); ++i) {
// Example:
std::cout << vec[i];
}
The code would be:
auto value = myvector[0];
myvector.erase(myvector.begin());
However, removing elements from the beginning (or somewhere in between) is slow, because it must copy the whole array. Access is fast though: vector allows random access (i.e. access by any explicit index) in O(1) (i.e. constant access time, i.e. very fast).
But another container structure instead of vector might make more sense for you, e.g. list or deque. Some STL implementations (or other framework) also have something like rope which is in many cases the best of both worlds.
There's nothing special to pay attention to. To insert, use
push_back, to extract, you need something like:
if ( !fifo.empty() ) {
ValueType results = fifo.front();
fifo.erase( fifo.begin() );
}
(Don't forget to check for empty before trying to remove an
element.)
An important point to remember is that both push_back and
in some cases erase can invalidate iterators, so you don't
want to keep iterators into the underlying vector hanging
around.
I ran into this problem when I tried to write out an new algorithm to reorder elements in std::vector. The basic idea is that I have std::list of pointters pointing into std::vector in such way that *list.begin() == vector[0], *(++list.begin()) == vector[1] and so on.
However, any modifications on list's element positions breaks the mapping. (Including appended pointers) When the mapping is broken the list's elements can be in random order but they point still into correct elements on vector. The task would be to reorder the elements in vector to correct the mapping.
Simplest method to do it (How I have done it now):
create new empty std::vector and resize it to equal size of the old vector.
iterate through the list and read elements from the old vector and write them into new vector. Set the pointer to point into new vector's element.
swap vectors and release the old vector.
Sadly the method is only useful when I need more capacity on the vector. It's inefficient when the current vector holding the elements has enough capacity to store all incoming elements. Appended pointers on the list will point into diffrent vector's storgate. The simple method works for this because it only reads from the pointers.
So I would want to reorder the vector "in place" using constant amount of memory. Any pointer that was not pointing into current vector's storgate are moved to point into current vector's storgate. Elements are simple structures. (PODs)
I'll try post an example code when I have time..
What should I do to achieve this? I have the basic idea done, but I'm not sure if it is even possible to do the reordering with constant amount of memory.
PS: I'm sorry for the (possibly) bad grammar and typos in the post. I hope it's still readable. :)
First off, why do you have a list of pointers? You might as well keep indices into the vector, which you can compute as std::distance(&v[0], *list_iter). So, let's build a vector of indices first, but you can easily adapt that to use your list directly:
std::vector<T> v; // your data
std::list<T*> perm_list; // your given permutation list
std::vector<size_t> perms;
perms.reserve(v.size());
for (std::list<T*>::const_iterator it = perm_list.begin(), end = perm_list.end(); it != end; ++it)
{
perms.push_back(std::distance(&v[0], *it));
}
(There's probably a way to use std::transform and std::bind, or lambdas, to do this in one line.)
Now to do the work. We simply use the cycle-decomposition of the permutation, and we modify the perms vector as we go along:
std::set<size_t> done;
for (size_t i = 0; i < perms.size(); while(done.count(++i)) {})
{
T tmp1 = v[i];
for (size_t j = perms[i]; j != i; j = perms[j])
{
T tmp2 = v[j];
v[j] = tmp1;
tmp1 = tmp2;
done.insert(j);
}
v[i] = tmp1;
}
I'm using the auxiliary set done to track which indices have already been permuted. In C++0x you would add std::move everywhere to make this work with movable containers.
In the C++ Primer book, Chapter (3), there is the following for-loop that resets the elements in the vector to zero.
vector<int> ivec; //UPDATE: vector declaration
for (vector<int>::size_type ix = 0; ix ! = ivec.size(); ++ix)
ivec[ix] = 0;
Is the for-loop really assigning 0 values to the elements, or do we have to use the push_back function?
So, is the following valid?
ivec[ix] = ix;
Thanks.
Is the for-loop really assigning 0
values to the elements? Or, we have to
use the push_back finction?
ivec[ix] =0 updates the value of existing element in the vector, while push_back function adds new element to the vector!
So, is the following valid?
ivec[ix] = ix;
It is perfectly valid IF ix < ivec.size().
It would be even better if you use iterator, instead of index. Like this,
int ix = 0;
for(vector<int>::iterator it = ivec.begin() ; it != ivec.end(); ++it)
{
*it = ix++; //or do whatever you want to do with "it" here!
}
Use of iterator with STL is idiomatic. Prefer iterator over index!
Yes, you can use the square brackets to retrieve and overwrite existing elements of a vector. Note, however, that you cannot use the square brackets to insert a new element into a vector, and in fact indexing past the end of a vector leads to undefined behavior, often crashing the program outright.
To grow the vector, you can use the push_back, insert, resize, or assign functions.
Using the array brackets the vector object acts just like any other simple array. push_back() increases its length by one element and sets the new/last one to your passed value.
The purpose of this for loop is to iterate through the elements of the vector.
Starting at element x (when ix is 0) up to the last element (when ix is ivec.size() -1).
On each iteration the current element of the vector is set to 9.
This is what the statement
ivec[ix] = 0;
does. Putting
ivec[ix] = ix;
in the for loop would set all the elements of the vector to their position in the vector. i.e, the first element would have a value of zero (as vectors start indexing from 0), the second element would have a value of 1, and so on and so forth.
Yes, assuming ix is a valid index, most likely: you have a vector of int though and the index is size_type. Of course you may want to purposely store -1 sometimes to show an invalid index so the conversion of unsigned to signed would be appropriate but then I would suggest using a static_cast.
Doing what you are doing (setting each value in the vector to its index) is a way to create indexes of other collections. You then rearrange your vector sorting based on a predicte of the other collection.
Assuming that you never overflow (highly unlikely if your system is 32 bits or more) your conversion should work.