STL way of creating/filling std::set from std::vector - c++

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

Related

for loop - iterate over specific elements

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;
}

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).

Copying a vector of pointers

I have a std::vector<A*> which I need to deep copy to another vector using A::Clone().
Instead of using handwritten loops, I was wondering whether I could use for_each or any Standard Library algorithm for this.
The appropriate algorithm is std::transform and you can turn member function invocation into a unary functor with std::mem_fun
Example:
#include <vector>
#include <functional>
#include <algorithm>
#include <iterator>
class X
{
public:
X* clone();
};
int main()
{
std::vector<X*> vec1, vec2;
std::transform(vec1.begin(), vec1.end(), std::back_inserter(vec2), std::mem_fun(&X::clone));
}
If the target vector is already the same size as the input range, you can pass vec2.begin() as the third argument. Use back_inserter if the target is empty (or you want to append to it).
Perhaps something like this would work:
class DeepCopy {
public:
A* operator() (A* aP) {
return aP->Clone();
}
}
int main()
{
vector<A*> vA;
vector<A*> vA2;
transform(vA.begin(), vA.end(), back_inserter(vA2), DeepCopy());
return 0;
}
You could use boost::ptr_vector<A> instead of std::vector<A*>.
This has a template parameter CloneAllocator, for which you could pass the relevant custom cloner.

What's the best way to sum the result of a member function for all elements in a container?

Let's say I have the following object:
struct Foo
{
int size() { return 2; }
};
What's the best way (most maintainable, readable, etc.) to get the total size of all objects in a vector<Foo>? I'll post my solution but I'm interested in better ideas.
Update:
So far we have:
std::accumulate and a functor
std::accumulate and a lambda expression
plain ol' for-loop
Are there any other workable solutions? Can you make something maintainable using boost::bind or std::bind1st/2nd?
In addition to your own suggestion, if your compiler supports C++0x lambda expressions, you can use this shorter version:
std::vector<Foo> vf;
// do something to populate vf
int totalSize = std::accumulate(vf.begin(),
vf.end(),
0,
[](int sum, const Foo& elem){ return sum + elem.size();});
Use std::accumulate and a functor.
#include <functional>
#include <numeric>
struct SumSizes : public std::binary_function<int, Foo, int>
{
int operator()(int total, const Foo& elem) const
{
return total + elem.size();
}
};
std::vector<Foo> vf;
// do something to populate vf
int totalSize = std::accumulate(vf.begin(),
vf.end(),
0,
SumSizes());
I find Boost iterators elegants, although they can be a bit verbose (range-based algorithms would make this better). In this case transform iterators can do the job:
#include <boost/iterator/transform_iterator.hpp>
//...
int totalSize = std::accumulate(
boost::make_transform_iterator(vf.begin(), std::mem_fn(&Foo::size)),
boost::make_transform_iterator(vf.end(), std::mem_fn(&Foo::size)),0);
Edit: replaced "boost::bind(&Foo::size,_1)" by "std::mem_fn(&Foo::size)"
Edit: I just found that the Boost.Range library has been updated to introduce range algorithms! Here is a new version of the same solution:
#include <boost/range/distance.hpp> // numeric.hpp needs it (a bug?)
#include <boost/range/numeric.hpp> // accumulate
#include <boost/range/adaptor/transformed.hpp> // transformed
//...
int totalSize = boost::accumulate(
vf | boost::adaptors::transformed(std::mem_fn(Foo::size)), 0);
Note: the performances are approximately the same (see my comment): internally, transformed uses transorm_iterator.
using C++11 (and beyond) range-based for loop
std::vector<Foo> vFoo;
// populate vFoo with some values...
int totalSize = 0;
for (const auto& element: vFoo) {
totalSize += element.size();
}
Here is the down-to-earth solution:
typedef std::vector<Foo> FooVector;
FooVector vf;
int totalSize = 0;
for (FooVector::const_iterator it = vf.begin(); it != vf.end(); ++it) {
totalSize += it->size();
}