for loop - iterate over specific elements - c++

I have the following data structure:
struct T
{
std::string name;
bool active;
};
Then I want to iterate over a vector of T but only for the active elements:
std::vector<T> myVector;
//fill vector
for(const auto& item: myVector)
{
if(!item.active)
{
continue;
}
//do something;
}
Is there any feature which allows achieve that without using if and/or continue statements ?

If you really want to eliminate the check and not just hide it, then use a separate container to store the indices of elements where active is true, and replace the for loop with one that goes through all the indices in the other container.
Make sure that the indices container is updated every time the vector changes.
#include <string>
#include <vector>
struct T
{
std::string name;
bool active;
};
int main()
{
std::vector<T> myVector;
using Index = decltype(myVector)::size_type;
std::vector<Index> indicesActive;
// ...
for (auto index : indicesActive)
{
auto const& item = myVector[index];
// ...
}
}
Whether that's worth the trouble is hard to say without knowing the context of your problem.
Note that you can probably replace your T with std::optional<std::string> if your compiler already supports std::optional.

Just write wrapper iterator class and range class.
https://gist.github.com/yumetodo/b0f82fc44e0e4d842c45f7596a6a0b49
This is an example that implement iterator wrapping iterator.
Another way is using Sprout.
sprout::optional is container type so that you can write like below:
std::vector<sprout::optional<std::string>> myVector;
//fill vector
for(auto&& e : myVector) for(auto&& s : e)
{
//do something;
}

Related

STL way of creating/filling std::set from std::vector

I want to create and fill a set from the contents of a member variable of each entry of a vector. This is what I am doing:
struct S { int i; };
int main()
{
std::vector<S*> structPtrs;
// code to fill the above vector
// create set from the above vector
std::set<int> setInts;
for (auto it = structPtrs.begin(); it != structPtrs.end(); ++it)
{
setInts.insert((*it)->i);
}
}
Is there an STL way to do it? Or via any available method(s) in <algorithm>?
You can always apply std::transform from the range defined by the vector onto the "range" defined by an std::inserter:
transform(begin(structPtrs), end(structPtrs),
inserter(setInts, end(setInts)), [] (S* s) {
return s->i;
});
That should be more than enough use of the standard library.
If you are willing to look beyond the standard library, there is also the option of using something like boost::transform_iterator, which will allow you to move the range transformation into the set's initialization:
auto transfomer = [](S* s) { return s->i; };
std::set<int> setInts(
boost::make_transform_iterator(begin(structPtrs), transfomer),
boost::make_transform_iterator(end(structPtrs), transfomer)
);
You could use std::transform with an appropriate lambda and an insert iterator:
std::transform(structPtrs.begin(), structPtrs.end(), std::inserter(setInts, setInts.end()),
[](S* sp) { return sp->i; });
But personally, I find a simple range for loop to be much easier to follow:
for (S* sp : structPtrs)
setInts.insert(sp->i);
There is one other way you can do this. If you add conversion operator to int to your struct you can just use range constructor directly
#include <iostream>
#include <set>
#include <vector>
using namespace std;
struct test {int i; operator int() {return i;}};
int main() {
vector<test> v;
v.push_back(test{433});
v.push_back(test{533});
set<int> s(v.begin(), v.end());
cout << *(++s.begin());
return 0;
}
https://www.ideone.com/qJwtwc

How to update the vectors in a std::unordered_map<std::string, std::vector<int>> without copying?

How to extend the below code to collect the all entry.score and not just keep the most recent one?
The code I have right now is:
std::unordered_map<std::string, float> container;
int main() {
// some code
for (auto entry : entries)
container[entry.name] = entry.score;
}
How to extend it to keep all entry.score whilst avoiding copy?:
std::unordered_map<std::string, std::vector<float>> container;
//vectors need to be updated using .push_back(entry.score)
int main() {
// some code
for (auto entry : entries)
container[entry.name] = ; //missing connection
}
container[entry.name] returns a std::vector<float>&. std::vector exposes push_back, which inserts an element into the vector.
Therefore you can do:
container[entry.name].push_back(entry);

std::transform on temporary object

I have a function returning a vector of objects, and I want to create a vector of members taken from these objects. I am using std::transform to do this. However, the code segfualts. GDB wasn't very helpful. Could anyone explain what is happening?
#include <algorithm>
#include <iostream>
using namespace std;
class Container
{
private:
string _id;
public:
Container(const string &str): _id(str) {}
const decltype(_id) &id() const {
return this->_id;
}
};
Container a{"hello"}, b{"world"};
vector<Container *> fn()
{
return {&a,&b};
}
int main() {
vector<string> ids;
const auto &elements = fn();
std::transform(elements.begin(), elements.end(), ids.begin(), [](const Container *container){ return container->id();});
}
ids.begin() is not an iterator into a valid range of length elements.size(), since ids is empty and elements has size 2.
You probably want std::back_inserter(ids) instead:
std::vector<std::string> ids;
std::transform(elements.begin(), elements.end(),
std::back_inserter(ids),
[](const Container *container){ return container->id();});
assert(ids.size() == elements.size());
vector<string> ids; is an empty vector and you are trying to add elements to it using a normal iterator. This is going to cause a segfault as you are going to access memory you do not own. What you need is a back_inserter to push_back the elements into the vector.
std::transform(elements.begin(), elements.end(), std::back_inserter(ids),
[](const Container *container){ return container->id();});

c++ std::vector search for value

I am attempting to optimize a std::vector "search " - index based iterating through a vector and returning and element that matches a "search" criteria
struct myObj {
int id;
char* value;
};
std::vector<myObj> myObjList;
create a few thousand entries with unique id's and values and push them to the vector myObjList.
What is the most efficient way to retrieve myObj that matches the id.
Currently I am index iterating like:
for(int i = 0; i < myObjList.size(); i++){
if(myObjList.at(i).id == searchCriteria){
return myObjList.at(i);
}
}
Note: searchCriteria = int. All the elements have unique id's.
The above does the job, but probably not the most efficient way.
The C++ standard library has some abstract algorithms, which give C++ a kind of functional flavour, as I call it, which lets you concentrate more on the criteria of your search than on how you implement the search itself. This applies to a lot of other algorithms.
The algorithm you are looking for is std::find_if, a simple linear search through an iterator range.
In C++11, you can use a lambda to express your criteria:
std::find_if(myObjList.begin(), myObjList.end(), [&](const myObj & o) {
return o.id == searchCriteria;
});
When not having C++11 available, you have to provide a predicate (function object (=functor) or function pointer) which returns true if the provided instance is the one you are looking for. Functors have the advantage that they can be parameterized, in your case you want to parameterize the functor with the ID you are looking for.
template<class TargetClass>
class HasId {
int _id;
public:
HasId(int id) : _id(id) {}
bool operator()(const TargetClass & o) const {
return o.id == _id;
}
}
std::find_if(myObjList.begin(), myObjList.end(), HasId<myObj>(searchCriteria));
This method returns an iterator pointing to the first element found which matches your criteria. If there is no such element, the end iterator is returned (which points past the end of the vector, not to the last element). So your function could look like this:
vector<myObj>::iterator it = std::find_if(...);
if(it == myObjList.end())
// handle error in any way
else
return *it;
Using std::find_if.
There's an example on the referenced page.
Here's a working example that more precisely fits your question:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct myObj
{
int id;
char* value;
myObj(int id_) : id(id_), value(0) {}
};
struct obj_finder
{
obj_finder(int key) : key_(key)
{}
bool operator()(const myObj& o) const
{
return key_ == o.id;
}
const int key_;
};
int main () {
vector<myObj> myvector;
vector<myObj>::iterator it;
myvector.push_back(myObj(30));
myvector.push_back(myObj(50));
myvector.push_back(myObj(100));
myvector.push_back(myObj(32));
it = find_if (myvector.begin(), myvector.end(), obj_finder(100));
cout << "I found " << it->id << endl;
return 0;
}
And, if you have C++11 available, you can make this even more concise using a lambda:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct myObj
{
int id;
char* value;
myObj(int id_) : id(id_), value(0) {}
};
int main ()
{
vector<myObj> myvector;
vector<myObj>::iterator it;
myvector.push_back(myObj(30));
myvector.push_back(myObj(50));
myvector.push_back(myObj(100));
myvector.push_back(myObj(32));
int key = 100;
it = find_if (myvector.begin(), myvector.end(), [key] (const myObj& o) -> bool {return o.id == key;});
cout << "I found " << it->id << endl;
return 0;
}
This isn't really an answer to your question. The other people who answered gave pretty good answers, so I have nothing to add to them.
I would like to say though that your code is not very idiomatic C++. Really idiomatic C++ would, of course, use ::std::find_if. But even if you didn't have ::std::find_if your code is still not idiomatic. I'll provide two re-writes. One a C++11 re-write, and the second a C++03 re-write.
First, C++11:
for (auto &i: myObjList){
if(i.id == searchCriteria){
return i;
}
}
Second, C++03:
for (::std::vector<myObj>::iterator i = myObjList.begin(); i != myObjList.end(); ++i){
if(i->id == searchCriteria){
return *i;
}
}
The standard way of going through any sort of C++ container is to use an iterator. It's nice that vectors can be indexed by integer. But if you rely on that behavior unnecessarily you make it harder on yourself if you should change data structures later.
If the ids are sorted you may perform binary search(there is also a function binary_search in stl). If they are not nothing will perform better, but still you may write your code in a shorter way using stl(use find_if).

Erase-remove idiom for deleting in a nested container? (deleting outer ones; C++ STL)

when i'm deleting from a non-nested container like a vector, i'm doing something like:
struct is_to_remove
{
is_to_remove(dynamic_bitset<>& x) : x(x) {}
const bool operator()(unsigned int id)
{
return x[id];
}
private:
dynamic_bitset<> x;
};
inline static void remove_elements_in_vector(vector<unsigned int>& vec, boost::dynamic_bitset<>& to_remove)
{
// use the erase-remove idiom to remove all elements marked in bitset
vec.erase( remove_if(vec.begin(), vec.end(), is_to_remove(to_remove)), vec.end() );
}
That is the so called erase-remove idiom.
Now, i have a second data strucurevector<vector<unsigned int> > or deque<vector<unsigned int> >, where i want to delete the outer container elements (which is itself a container of the inner type) according to a bitset.
Is it possible to use the erase-remove idiom on this nested container types?
If it is, how is it possible?
Are there restrictions? (like: vec of vec is possible, but not deque of vec)?
My first and naive approach was the following. I assumed that remove_if is iterating sequentially and in order over the elements and deciding one after the other. Is that a wrong assumption?
struct is_to_remove_new
{
is_to_remove_new(dynamic_bitset<>& x, unsigned int index) : x(x), index(index) {}
const bool operator()(vector<unsigned int> & vec)
{
return x[index++];
}
private:
dynamic_bitset<> x;
unsigned int index;
};
inline static void remove_elements_in_vectorvector(vector<vector<unsigned int> >& vec, boost::dynamic_bitset<>& to_remove)
{
// use the erase-remove idiom to remove all elements marked in bitset
vec.erase( remove_if(vec.begin(), vec.end(), is_to_remove_new(to_remove, 0)), vec.end() );
}
The result is wrong, therefore i'm looking for a correct solution here. I suppose i assumed some things, which aren't guaranteed. For me, the base question is the following: How to get the identity of the inner container for checking if it is to remove..
My naive approach posted above just counts and assumes a sequential processing.
Thanks for your help.
Sascha
Update and Warning
For a vector o vectors, Stas solution is working great. But i think this solution will not work for a deque of vectors because a deque isn't saved in a contiguous way. This means, that the calculation of the index in the functor fails.
Can anyone verify that?
It doesn't matter which elements are in the vector. If you define which of them should be removed, they will be removed.
As you said, the question is how to identify an element in a vector. The most obvious answer is by its index [0; vector_size - 1].
Thanks to a vector, you can easily get an element index, by the element itself.
std::vector<std::string> vec;
vec.push_back("zero");
vec.push_back("one");
vec.push_back("two");
std::string& elem = vec[2];
int index = std::distance(&vec[0], &elem); // get element index by element itself
// index == 2
So, you can easily identify vector elements by indices inside remove_if algorithm predicate. Take a look at the following example. It is pretty silly and use hard-coded std::bitset<6>, but this is just an illustration:
#include <vector>
#include <string>
#include <bitset>
struct ToRemove
{
ToRemove(std::vector<std::string>& vec, const std::string& mask)
: m_vec(vec)
, m_mask(mask)
{}
bool operator()(std::string& obj)
{
const int index = std::distance(&m_vec[0], &obj);
return m_mask[index];
}
std::vector<std::string>& m_vec;
std::bitset<6> m_mask;
};
Usage
int main (int argc, char const *argv[])
{
std::vector<std::string> vec;
vec.push_back("zero");
vec.push_back("one");
vec.push_back("two");
vec.push_back("three");
vec.push_back("four");
vec.push_back("five");
std::string mask = ("010011"); // Remove "zero", "one" and "four"
vec.erase(remove_if(vec.begin(), vec.end(), ToRemove(vec, mask)), vec.end());
for (unsigned i = 0; i < vec.size(); ++i)
{
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
Result
two three five