I want to reuse a std::vector within a for loop. However, I need the vector to be empty for each iteration step of the for loop.
Question: How can I empty a vector rapidly without changing its capacity in the most efficient way?
What I used so far is
std::vector<int> myVec;
for(int i=0; i<A_BIG_NUMBER; ++i) {
std::vector<T>().swap(myVec);
myVec.reserve(STANDARD_MAXIMUM);
/// .. doing business
}
Cheers!
Solution:
Thanks for the answers, here is how I implemented (checked) it:
#include <vector>
#include <iostream>
int main() {
int n = 10;
std::vector< int > myVec;
myVec.reserve(n);
for(int j=0; j<3; ++j) {
myVec.clear();
for(int i=0; i<n; ++i) {
myVec.push_back(i);
}
for(int i=0; i<myVec.size(); ++i) {
std::cout << i << ": " << myVec[i] << std::endl;
}
}
return 0;
}
EDIT: changed from operator[] to push_back.
Use vector::clear method. It will clear the content without reducing its capacity.
myVec.clear();
This is equivalent to myVec.erase(myVec.begin(), myVec.end()).
To retain the current size of a vector with default values for its content, you can assign default values to the vector. In the case of a vector of ints, you can do the following:
myVec.assign( myVec.size(), 0 );
use clear method as below:
std::vector<int> myVec;
for(int i=0; i<A_BIG_NUMBER; ++i)
{
std::vector<T>().swap(myVec);
myVec.reserve(STANDARD_MAXIMUM);
/// .. doing business
myVec.clear();
}
Answer based on OP's solution:
The normal approach for containers is to start with an empty container and fill it up as needed with an exception for std::vector where you can reserve space eventhough there are still no objects in the container.
If you want a different approach where an "empty container" would be a container of default objects that you can access like an array (only works with std::vector and std::deque), then you need to start with resize() and you can "clean up" with fill:
int n = 10;
std::vector<int> myVec;
myVec.resize(n);
myVec[4] = 5;
std::cout << myVec[4] << "\n";
std::fill(myVec.begin(), myVec.end(), int()); // ofcourse for int, you can use 0 instead of int()
std::cout << myVec[4] << "\n";
Related
I have a std::vector<string> where each element is a word. I want to print the vector without repeated words!
I searched a lot on the web and I found lots of material, but I can't and I don't want to use hash maps, iterators and "advanced" (to me) stuff. I can only use plain string comparison == as I am still a beginner.
So, let my_vec a std::vector<std::string> initialized from std input. My idea was to read all the vector and erase any repeated word once I found it:
for(int i=0;i<my_vec.size();++i){
for (int j=i+1;j<my_vec.size();++j){
if(my_vec[i]==my_vec[j]){
my_vec.erase(my_vec.begin()+j); //remove the component from the vector
}
}
}
I tried to test for std::vector<std::string> my_vec{"hey","how","are","you","fine","and","you","fine"}
and indeed I found
hey how are you fine and
so it seems to be right, but for instance if I write the simple vector std::vector<std::string> my_vec{"hello","hello","hello","hello","hello"}
I obtain
hello hello
The problem is that at every call to erase the dimension gets smaller and so I lose information. How can I do that?
Minimalist approach to your existing code. The auto-increment of j is what is ultimately breaking your algorithm. Don't do that. Instead, only increment it when you do NOT remove an element.
I.e.
for (int i = 0; i < my_vec.size(); ++i) {
for (int j = i + 1; j < my_vec.size(); ) { // NOTE: no ++j
if (my_vec[i] == my_vec[j]) {
my_vec.erase(my_vec.begin() + j);
}
else ++j; // NOTE: moved to else-clause
}
}
That is literally it.
You can store the element element index to erase and then eliminate it at the end.
Or repeat the cycle until no erase are performed.
First code Example:
std::vector<int> index_to_erase();
for(int i=0;i<my_vec.size();++i){
for (int j=i+1;j<my_vec.size();++j){
if(my_vec[i]==my_vec[j]){
index_to_erase.push_back(j);
}
}
}
//starting the cycle from the last element to the vector of index, in this
//way the vector of element remains equal for the first n elements
for (int i = index_to_erase.size()-1; i >= 0; i--){
my_vec.erase(my_vec.begin()+index_to_erase[i]); //remove the component from the vector
}
Second code Example:
bool Erase = true;
while(Erase){
Erase = false;
for(int i=0;i<my_vec.size();++i){
for (int j=i+1;j<my_vec.size();++j){
if(my_vec[i]==my_vec[j]){
my_vec.erase(my_vec.begin()+j); //remove the component from the vector
Erase = true;
}
}
}
}
Why don't you use std::unique?
You can use it as easy as:
std::vector<std::string> v{ "hello", "hello", "hello", "hello", "hello" };
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end());
N.B. Elements need to be sorted because std::unique works only for consecutive duplicates.
In case you don't want to change the content of the std::vector, but only have stable output, I recommend other answers.
Erasing elements from a container inside a loop is a little tricky, because after erasing element at index i the next element (in the next iteration) is not at index i+1 but at index i.
Read about the erase-remove-idiom for the idomatic way to erase elements. However, if you just want to print on the screen there is a much simpler way to fix your code:
for(int i=0; i<my_vec.size(); ++i){
bool unique = true;
for (int j=0; j<i; ++j){
if(my_vec[i]==my_vec[j]) {
unique = false;
break;
}
if (unique) std::cout << my_vec[i];
}
}
Instead of checking for elements after the current one you should compare to elements before. Otherwise "bar x bar y bar" will result in "x x bar" when I suppose it should be "bar x y".
Last but not least, consider that using the traditional loops with indices is the complicated way, while using iterators or a range-based loop is much simpler. Don't be afraid of new stuff, on the long run it will be easier to use.
You can simply use the combination of sort and unique as follows.
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<std::string> vec{"hey","how","are","you","fine","and","you","fine"};
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end() ), vec.end());
for (int i = 0; i < vec.size(); i ++) {
std::cout << vec[i] << " ";
}
std::cout << "\n";
return 0;
}
So I have a struct
struct float3
{
float x, y, z;
};
and I am trying to create a function to take the x,y,z values and map them to keys 0, 1, 2 for their respective dimension. I wrote the code below but it throws a bad alloc_exception. It appears like I'm running out of memory.
KdTree::float2map(std::vector<float3>& data)
{
std::vector<std::map<int, float> > m_pnts;
int cnt = 0;
for(int i = 0; i = data.size(); i++)
{
std::map<int, float> tmp;
tmp.insert(std::make_pair(0, data[i].x));
tmp.insert(std::make_pair(1, data[i].y));
tmp.insert(std::make_pair(2, data[i].z));
m_pnts.push_back(tmp);
std::cout << m_pnts.size() << std::endl;
}
}
return m_pnts;
}
I'm still fairly new to C++ so I'm sure there are many other ways to do this or to optimize this approach. The problem is I have to do this one 33,914,095 float3s and I can't think of another way to achieve this. Any help would be greatly appreciated.
Look at this line of code:
for(int i = 0; i = data.size(); i++)
You are setting i to data.size() in the condition of the for statement.
This will cause an out of bounds access because data.size() will become the index to data vector whose elements are indexed from 0 to data.size() - 1.
That was probably not your intention.
Use a proper condition in the for loop. It should be:
for(int i = 0; i < data.size(); i++)
Or better still, use a range-based for loop which help in avoiding such bugs:
for(const auto& ele: data)
{
std::map<int, float> tmp;
tmp.insert(std::make_pair(0, ele.x));
tmp.insert(std::make_pair(1, ele.y));
tmp.insert(std::make_pair(2, ele.z));
m_pnts.push_back(tmp);
std::cout << m_pnts.size() << std::endl;
}
I have an array and a for loop. I want the for loop to stop depending on the number of elements the array has.
For example if I have an int array []={1,0,1,0,1}
I want the loop to execute code 5 times. Similar to the function for strings .length() but for integers. An example with a simple code would be the best answer :)
like this pseudocode:
for(int b=0;b<array-length;b++)
Unless you need the index, the following works fine:
int ar[] = { 1, 2, 3, 4, 5 };
for (auto i : ar) {
std::cout << i << std::endl;
}
Since the question is tagged with C++, I'll have to suggest std::vector as the best solution. (Also for the future)
Look into this: std::vector
So for you this'd be like the following:
std::vector<int> array {1,0,1,0,1};
for(int i = 0; i < array.size(); i++)
...
Or in the worst case an std::array if you don't want the features of a vector.
See also: std::array
To find length of an array, use this code
int array[5];
std::cout << "Length of array = " << (sizeof(array)/sizeof(*array)) << std::endl;
So in your case, it will be for example:
int array[5];
for(int b=0; b < sizeof(array)/sizeof(*array); b++){
std::cout << array[b] << std::endl;
}
How do I use a for loop to erase elements from a vector by its index ? I am getting a vector out of range error. I have a sample code below.
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
for(int i = 0; i < to_erase.size(); i++) {
data.erase(data.begin() + to_erase[i]);
}
I think it is because the size of my vector reduces through every iteration therefore it cannot access index 2.
You would normally employ the erase–remove idiom to delete multiple elements from a vector efficiently (erasing them one by one is generally less efficient, and, as you’ve seen, not always trivial). In its most general form, the idiom looks like this:
data.erase(remove_algorithm(begin(data), end(data)), end(data));
In your case, the remove_algorithm is based off indices in another vector, so we need to provide those as well:
data.erase(
remove_indices(begin(data), end(data), begin(to_erase), end(to_erase)),
end(data));
Unfortunately, such an algorithm isn’t contained in the standard library. However, it’s trivial to write yourself1:
template <typename It, typename It2>
auto remove_indices(It begin, It end, It2 idx_b, It2 idx_e) -> It {
using idx_t = typename std::iterator_traits<It2>::value_type;
std::sort(idx_b, idx_e, std::greater<idx_t>{});
for (; idx_b != idx_e; ++idx_b) {
auto pos = begin + *idx_b;
std::move(std::next(pos), end--, pos);
}
return end;
}
Here, we first sort the indices to be removed from largest to smallest. Next, we loop over those indices. We then (maximally efficiently) move all elements between the current position (to be deleted) and the end of the vector forward by one. Subsequently, the end is decreased by one (to account for the fact that an element got deleted).
Live code
1 *Ahem* Once you’ve removed all the silly typos in your code.
I think it is because the size of my vector reduces through every iteration
Yes!
You could do it by keeping an extra variable, which counts the elements that are deleted, like this:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
int count_removed = 0;
for(unsigned int i = 0; i < to_erase.size(); i++)
data.erase(data.begin() + to_erase[i] - count_removed++);
for(unsigned int i = 0; i < data.size(); ++i)
cout << data[i] << "\n";
return 0;
}
Output:
3
I had the same problem when I first used std::erase(), good question, +1.
Deleting a collection of elements meanwhile you iterate, is unsafe and probalby expensive. I would suggest that each element that meets your criteria gets swapped with an element at the end. (at the end because will be cheaper to erase from the end. You can keep track of how much back you came from the end of the vector (based on the number of swap), and break early our of the loop. Now based on how many elements you swapped you can do something like:
data.resize(data.size() - reverse_counter);
or
int pos = data.size() - reverse_counter;
data.erease(data.begin()+pos, data.end();
It is sudo code just to explain the idea.
As mentioned in the reference, erase not at the end cause re-allocation, which is expensive. Something worth keep in mind:
http://www.cplusplus.com/reference/vector/vector/erase/
I think this is a bad design, because you will change the for loop invariant and will need a lot of workaround to make this happen. Anyway, if you really want to use a for loop, you MAY flag what you whant to delete and run a stl remove_if, something like:
#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
using namespace std;
int main() {
vector<int> to_erase = {0, 1, 2};
vector<int> data = {3, 3, 3, 3};
cout << "Before:\n" ;
for(int i=0; i<data.size(); i++)
cout << i << "\t";
cout << endl;
for(int i=0; i<data.size(); i++)
cout << data[i] << "\t";
cout << endl;
for(int i = 0; i < to_erase.size(); i++) {
//data.erase(data.begin() + to_erase[i]);
data[i] = numeric_limits<int>::max();
}
data.erase(remove_if(data.begin(),
data.end(),
[](int i){return i==numeric_limits<int>::max();}), data.end());
cout << "Next:\n" ;
for(int i=0; i<data.size(); i++)
cout << i << "\t";
cout << endl;
for(int i=0; i<data.size(); i++)
cout << data[i] << "\t";
return 0;
}
i am currently trying to learn some C++ and now i got stuck in an exercise with vectors. So the task is to read ints from a text file and store them in the vector which should be dynamic.
I guess there is something wrong with the while-loop?
If I start this, the program fails and if I set the vector size to 6, I get
6 0 0 0 0 0 as output.
Thanks for any hints.
int main()
{
const string filename = "test.txt";
int s = 0;
fstream f;
f.open(filename, ios::in);
vector<int> v;
if (f){
while(f >> s){
int i = 0;
v[i] = s;
i = i+1;
}
f.close();
}
for(int i = 0; i < 6; i++){
cout << v[i] << "\n";
}
}
You don't grow the vector. It is empty and cannot hold any ints. You'll need to either resize it every time you want to add another int or you use push_back which automatically enlarges the vector.
You set i = 0 for every iteration so you would change the first value of the vector every iteration instead of the next one.
Go for:
v.push_back(s);
in your loop and
for(int i = 0; i < v.size(); i++) { // ...
Remark:
You normally don't hardcode vector sizes/bounds. One major point about using std::vector is its ability to behave dynamically with respect to its size. Thus, the code dealing with vectors should not impose any restrictions about the size of the vector onto the respective object.
Example:
for(int i = 0; i < 6; i++){ cout << v[i] << "\n"; }
requires the vector to have at least 6 elements, otherwise (less than 6 ints) you access values out of bounds (and you potentially miss elements if v contains more than 6 values).
Use either
for(int i = 0; i < v.size(); i++){ cout << v[i] << "\n"; }
or
for(std::vector<int>::const_iterator i = v.begin(); i != v.end(); ++i)
{
cout << *i << "\n";
}
or
for(auto i = v.begin(); i != v.end(); ++i)
{
cout << *i << "\n";
}
or
for(int x : v){ cout << x << "\n"; }
or
for(auto && x : v){ cout << x << "\n"; }
or
std::for_each(v.begin(), v.end(), [](int x){ std::cout << x << "\n"; });
or variants of the above which possibly pre-store v.size() or v.end()
or whatever you like as long as you don't impose any restriction on the dynamic size of your vector.
The issue is in the line i= 0. Fixing that will give an issue in the line v[i] = s.
You always initialise i to 0 in the while loop, and that is responsible for the current output. You should shift it out of the while loop.
After fixing that, you have not allocated memory to that vector, and so v[i] doesn't make sense as it would access memory beyond bounds. This will give a segmentation fault. Instead, it should be v.push_back(i), as that adds elements to the end of a vector, and also allocates memory if needed.
If you are using std::vector you can use v.push_back(i) to fill this vector
Error is this line int i = 0;
because you declare i=0 every time in while-loop.
To correct this move this line outside from loop.
Note: this will work, if you declare v like normal array for example int v[101]
When you use std vectors you can just push element at the end of vector with v.push_back(element);
v[i] = s; //error,you dont malloc room for vector
change into : v.push_back(s);