Iterate through a pointer set that contains string vectors in C++ - c++

This is my declaration of the set:
set< vector<string> >* tuples = new set< vector<string> >();
And this is how I am trying to iterate through it:
for(set< vector<string> >::iterator it = tuples->begin(); it != tuples->end(); it++){
if(it[column] == value){
rowResults->insert(*it);
}
}
but I get an error
no match for ‘operator[]’ (operand types are ‘std::set<std::vector<std::__cxx11::basic_string<char> > >::iterator {aka std::_Rb_tree_const_iterator<std::vector<std::__cxx11::basic_string<char> > >}’ and ‘int’)
if(it[column] == value){
^

You're applying [] to the iterator instead of to the object to which it points. You need to dereference the iterator (and mind operator precedence!):
for(set< vector<string> >::iterator it = tuples->begin(); it != tuples->end(); ++it){
if((*it)[column] == value){
rowResults->insert(*it);
}
}
Note that with iterators, it's better to use ++it instead of it++ in loops, since the latter can be less efficient under insufficient optimisation.

it is an iterator, not the vector object itself. To access the vector object just use *it
Even better: get rid of the confusing iterator type by defining a reference (here constant ref since we don't seem to need a non-const) to the element itself.
for(set< vector<string> >::iterator it = tuples->begin(); it != tuples->end(); it++){
const vector<string> &v = *it; // more readable
if(v[column] == value){
rowResults->insert(v);
}
}
as no decent C++ answer cannot not mention the "new" C++11, note that if you use -std=c++11 option, the syntax is much better to iterate on a list
for(auto v : *tuples)
{
if(v[column] == value){
rowResults->insert(v);
}
}

You may avoid iterator with something like:
std::set<std::vector<std::string>>
computeRowResult(const std::set<std::vector<std::string>>& input,
int column,
const std::string& value)
{
std::set<std::vector<std::string>> rowResults;
for (const auto& v : input) {
if (v[column] == value) {
rowResults.insert(v);
}
}
return rowResults;
}
or avoiding the manual loop with
std::set<std::vector<std::string>>
computeRowResult(const std::set<std::vector<std::string>>& input,
int column,
const std::string& value)
{
std::set<std::vector<std::string>> rowResults;
std::copy_if(input.begin(), input.end(),
std::inserter(rowResults, rowResults.end()),
[&](const auto& v) { return v[column] == value; });
return rowResults;
}
Demo

Related

how to make find() function using vector (class)

there is class like as
class C_Service
{
public :
C_Service(); {memset(this, 0, sizeof(*this));}
C_Service(int type, int idx) {memset(this, 0, sizeof(*this)); this->type = type; this->idx = idx;}
bool operator==(const C_Service& svc) const { return (this->type == svc.type && this->idx == svc.idx);}
word type;
word idx;
dword aId;
dword bId;
char* name;
};
I used test code as below,
void vec_find(int type, int idx)
{
vector<C_Service*> vec;
// added several items in vector vec
...
vector<C_Service*>::iterator iter;
C_Service cSvc(type, idx);
iter = find(vec.begin(), vec.end(), &cSvc);
C_Service* findsvc = *iter;
if(findsvc)
printf("FOUND : type(%d), idx(%d), name(%s)\n", findsvc->type, findsvc->idx, findsvc->name);
else
printf("Not FOUND!!\n");
}
then, it give "Not FOUND!!" even set correct value.
I found something wrong and trying change..
iter = find(vec.begin(), vec.end(), &cSvc);
to
iter = find(vec.begin(), vec.end(), cSvc);
remove "&"
then it give compile error message
/libcxx/algorithm: In instantiation of '_InputIterator
std::__1::find(_InputIterator, _InputIterator, const _Tp&) [with
_InputIterator = std::__1::__wrap_iter; _Tp = C_Service]':
no match for 'operator==' (operand types are 'C_Service*' and 'const
C_Service')
I searched that when I use find() function in Container, It can use operator==
but, I can't get a goal..T.T
What is my fault?
The problem is that your vec is a vector of pointers, not a vector of C_Service objects.
Thus
find(vec.begin(), vec.end(), &cSvc)
checks whether the address of the cSvc variable is contained within vec (which it's not because you just created cSvc so it can't be referenced from anywhere else). It does not use your operator== at all, it just compares pointers.
To fix it, you can either change vec to be a std::vector<C_Service> and do
find(vec.begin(), vec.end(), cSvc)
or pass a custom predicate to find_if, where you can dereference your pointers manually:
find_if(vec.begin(), vec.end(), [&](const C_Service *p) { return *p == cSvc; })

l-value specifies const object when using std::make_pair

struct MapInserter
{
private:
int count;
public:
explicit MapInserter()
: count(0)
{
}
std::pair<int, std::string> operator()(std::string& value)
{
return std::make_pair(count++, value);
}
};
vector<std::string> words = { "one", "two", "three","four","five" };
std::map<int, std::string> map;
MapInserter inserter;
transform(words.begin(), words.end(), map.begin(), inserter);
for (auto it = map.begin(), end = map.end(); it != end; ++it)
cout << it->first << " : " << it->second << endl;
return 0;
that's the code. VS returns a compile error regarding l-value specifies const object.
Clicking the error moves you to the following code in a file named utility
template<class _Other1,
class _Other2>
_Myt& operator=(pair<_Other1, _Other2>&& _Right)
{ // assign from moved compatible pair
first = _STD forward<_Other1>(_Right.first);
second = _STD forward<_Other2>(_Right.second);
return (*this);
}
at first, i've had the operator() take const std::string& so I removed the const, because it's obviously talking about the make_pair function. But it still hasn't gone away. Can anyone point me to what this error is about?
The problem is that std::transform() will try to assign to existing elements of the target container. Keys of a map are constant and cannot be assigned to, which is why you're getting a compiler error. But even if they were, you'd get undefined behavior at run-time here, because the target container is empty, and std::transform() would expect it to contain as many elements as the input range.
You should use std::inserter() to create an inserter iterator, like so:
vector<std::string> words = { "one", "two", "three","four","five" };
std::map<int, std::string> map;
MapInserter inserter;
transform(words.begin(), words.end(), std::inserter(map, map.begin()), inserter);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here is a live example.
Moreover, taking the value string by mutable lvalue reference in the call operator of your MapInserter is not a good idea: you don't want the argument to be modified, so you should either take it by const& or - my advice - take it by value and then move it into the returned pair, like so:
std::pair<int, std::string> operator()(std::string value)
{
return {count++, std::move(value)};
}
Since std::pair's constructor is not explicit, you do not even need the call to std::make_pair() in this case.

Should I avoid using pointers here?

I have this simple code:
std::vector<std::map<double,double>> v;
//populate v
//we know each map already has correct key order (enforced by c++)
//but i also want to make sure the different maps have correct key order
//this is how I do it using a pointer:
const double *last_key = nullptr;
for (const auto &map : v)
{
if (map.size() > 0) //ignore empty maps
{
if (last_key)
{
const auto &new_key = map.cbegin()->first;
if (!(*last_key < new_key))
throw std::runtime_error("invalid key order");
}
last_key = &(--map.cend())->first;
}
}
Is this a good use for pointers? How would you do it instead?
The only real alternative I know (if I want to avoid pointers) is to do this:
double last_key;
bool last_key_has_been_set = false;
This works, but it requires that the key is default constructible and it involves unnecessary copying of keys (problem for different key types than double).
OK, since I now (think I) understand what your code is about, here's my take on it:
auto iter = v.begin();
auto end = v.end();
while (iter != end && iter->empty())
++iter;
if (iter != end)
{
while (true) // loop and a half
{
auto next = iter+1; // at this point, we know iter != end
while (next != end && next->empty())
++next;
if (next == end)
break;
auto lhslast = lhs.end();
--lhslast;
if (lhslast->first > next->begin()->first)
throw std::runtime_error("invalid key order");
iter = next;
}
}
Edit:
The code above can be further improved using another algorithm:
Replace
while (iter != end && iter->empty())
++iter;
with
iter = std::find_if(iter, end,
[](std::map<double, double> const& m) { return m.empty(); });
and analogous for the next loop.
Another option is to notice that if it were not empty maps, you could just use adjacent_find. Therefore another option is to make use of Boost's filter_iterator to get rid of the empty maps. Thus do
#include <boost/iterator/filter_iterator.hpp>
struct is_not_empty
{
template<typename Container> bool operator()(Container const& c) const
{
return !c.empty();
}
};
and then at the place of your code
auto fbegin = boost::make_filter_iterator(is_not_empty(), v.begin(), v.end());
auto fend = boost::make_filter_iterator(is_not_empty(), v.end(), v.end());
if (std::adjacent_find(fbegin, fend,
[](std::map<double, double> const& lhs,
std::map<double, double> const& rhs) -> bool
{
auto lhslast = lhs.end();
--lhslast;
return lhslast->first > rhs.begin()->first;
}) != fend)
throw std::runtime_error("invalid key order");
The filter iterator makes sure that only the non-empty maps are considered.
I don't think there is a suitable predefined algorithm in the standard library that does this. In particular, std::adjacent_find could be used for this if you were to define a relatively complex and stateful predicate for it, but that would really amount to misusing std::adjacent_find as some kind of replacement for std::for_each, i.e. it would not have much to do with the original purpose of std::adjacent_find.
However, instead of naked pointers, you should be using iterators. I'd also suggest putting the checking code into a separate function, perhaps named check. Here is what I would suggest:
#include <vector>
#include <map>
#include <iostream>
bool check(const std::vector<std::map<double,double>> &v)
{
/* Fast-forward to the first non-empty entry. */
auto it = begin(v);
for( ; it != end(v) ; ++it)
if (!it->empty())
break;
/* We might be done by now. */
if (it == end(v))
return true;
/* Else, go through the remaining entries,
skipping empty maps. */
auto prev = it->end();
advance(prev,-1);
++it;
for ( ; it != end(v) ; ++it)
{
if (!it->empty())
{
if (it->begin()->first < prev->first)
return false;
prev = it->end();
advance(prev,-1);
}
}
return true;
}
int main()
{
std::vector<std::map<double,double>> v;
/* Two entries for the vector, invalid order. */
v.push_back({ {1.0,1.0} , {2.0,4.0} });
v.push_back({ {3.0,9.0} , {1.0,16.0} });
if (!check(v))
throw std::runtime_error("Invalid order of the maps in the vector.");
return 0;
}
Note: It would be even more C++-like (or, at least more like the algorithms in the standard library) if you were to define the check function as an algorithm that takes a range of iterators, rather than a reference to a container, as argument. Rewriting the function to match this concept is straight-forward.
Note 2: The advantage of using iterators instead of naked pointers is that you get a better and cleaner abstraction of what you need: Something that references an item in the map, whereas a double* pointer could be pointing to all kinds of things. However, there is also a disadvantage of using iterators: If you were to modify your algorithm such that it alters maps while iterating through the vector, the iterator may be invalidated, whereas the pointer would not (unless you delete the element it points to). (The pointers might be invalidated if you alter the vector, though.)
But as long as the checking procedure is only used for checking and nothing else (which my code indicates by putting the code into a separate function dedicated to this purpose, and by taking the vector as a const-reference), iterator invalidation is not an issue.
This uses a C++1y feature (std::tr2::optional), but should work with any container and any ordering on the elements of the containers:
struct compare_key_order {
template<typename LHS, typename RHS>
bool operator()( LHS const& lhs, RHS const& rhs ) {
return lhs.first < rhs.first;
}
};
template<typename ContainerOfContainers, typename Ordering>
bool are_container_endpoints_ordered( ContainerOfMaps&& meta, Ordering&& order=compare_key_order() )
{
using std::begin; using std::end;
// or boost::optional:
std::tr2::optional< decltype( begin(begin(meta)) ) > last_valid;
for( auto&& Map : std::forward<Meta>(meta) ) {
auto b = begin(Map);
auto e = end(Map);
if (b==e)
continue;
if (last_valid)
if (!order( **last_valid, *b ))
return false;
last_valid = e;
}
return true;
}
optional is a prettier, less error prone way of dealing with the "this element may or may not exist" than a pointer-that-can-be-nullptr. If you are using boost or have access to a std::tr2::optional (or you are reading this in the future, when std::optional exists), it is a better idea than a pointer.
You could also move the "is there a last_valid out of state and into program code location:
struct compare_key_order {
template<typename LHS, typename RHS>
bool operator()( LHS const& lhs, RHS const& rhs ) {
return lhs.first < rhs.first;
}
};
template<typename ContainerOfContainers, typename Ordering>
bool are_container_endpoints_ordered( ContainerOfMaps&& meta, Ordering&& order=compare_key_order() )
{
using std::begin; using std::end;
auto it = begin(meta);
while( it != end(meta) && (begin(*it) == end(*it)) {
++it;
}
if ( it == end(meta) )
return true;
auto last_valid_end = end(*it);
for( ++it; it != end(meta); ++it ) {
auto b = begin(*it);
auto e = end(*it);
if (b==e)
continue;
if (!order( *last_valid_end, *b ))
return false;
last_valid = e;
}
return true;
}
this would let the same algorithm run on vectors-of-vectors-of-pairs, or even check if vectors-of-vectors have sorted endpoints (with a different order).
Best noted comment gives the answer : use adjacent_find.
First a little bit of logic. If there is n < m indexes verifying key[n] > key[m], then an index i exists, n <= i < m where key[i] > key[i+i].
You can demonstrate this with absurdity reasoning : If there is no such i, then for all i between n and m we have order, and because order relation is transitive, key[n] <= key[m] : absurd.
This means, ignoring the empty maps, if you have bad order on your keys, then you have two adjacent keys in bad order.
So your algorithm shoud be:
typedef map<double, double> map_t;
vector<map_t> v;
remove_if(v.begin(), v.end(), [](map_t const& m){return m.empty();});
if(adjacent_find(v.begin(), v.end(), [](map_t const& l, map_t const& r)
{
return (--l.cend())->first > r.cbegin()->first;
}) != v.end())
throw std::runtime_error("invalid key order");
This is, of course, if you can remove the empty maps from your vector first. (we can assume that, as empty maps may not be so meaningful, but it depends on the whole situation, for sure).

can't assign iterator to constant input vector in a binary function

I am trying to write a binary function that takes two vectors(of the same length) and adds them by value. For some reason the following code does not work:
struct Plusval : binary_function <std::vector<int>,std::vector<int>,std::vector<int> >
{
std::vector<int> operator() (const std::vector<int>& x, const std::vector<int>& y) const
{
std::vector<int> ret(x);
std::vector<int>::iterator itx,ity;
ity=y.begin();
for (itx=ret.begin();itx<ret.end();++itx)
{
ret[*itx]+=y[*ity];
++ity;
}
return ret;
}
};
I get an error that I can't do ity=y.begin()
However, the following code does work
struct Plusval : binary_function <std::vector<int>,std::vector<int>,std::vector<int> >
{
std::vector<int> operator() (const std::vector<int>& x, const std::vector<int>& y) const
{
std::vector<int> ret(x);
std::vector<int> yloc(y);
std::vector<int>::iterator itx,ity;
ity=yloc.begin();
for (itx=ret.begin();itx<ret.end();++itx)
{
ret[*itx]+=yloc[*ity];
++ity;
}
return ret;
}
};
Obviously, the second version will take longer (since it has to copy an additional vector). Is it because the input is a const vector? If it is, is there any reason it needs to be? Note that I am planning on using this function as an input to the allreduce() function in boost::mpi if that makes any difference
You define ity as vector::iterator y is const and returns a const_iterator.
What is more important is: Don't use binary_function. The adapters have been deprecated.
Also, your function does not do what you want. *itx returns the value stored at the position pointed to by itx and you use it to index into the you intend to return vector.
I would write this with a binary transform.
std::vector<int> res;
std::transform(begin(x), end(x), begin(y),
std::back_inserter(res), std::plus<int>());
The error is that you cannot use non-const iterators with a const container, as that would break const-correctness. You should use std::vector<int>::const_iterator on the second argument.
Other than that, the implementation in the first block does not do what you claim it does.You are iterating over the container and using the stored values to index into the container and update there. If you actually want to add the values from the two containers, it is much simpler than that:
struct PlusVal
{
std::vector<int> operator()( std::vector<int> lhs, std::vector<int> const& rhs )
{
assert( lhs.size() == rhs.size() );
for (std::vector<int>::size_type i = 0; i < lhs.size; ++i )
lhs[i] += rhs[i];
return lhs;
}
};
If you want to do that with iterators, it is again similarly simple:
struct PlusVal
{
std::vector<int> operator()( std::vector<int> lhs, std::vector<int> const& rhs )
{
assert( lhs.size() == rhs.size() );
std::vector<int>::iterator it = lhs.begin(), end = lhs.end();
std::vector<int>::const_iterator rit = rhs.begin();
while ( it != end )
*it++ += *rit++;
return lhs;
}
};
You're looking for the std::vector::const_iterator type
std::vector<int> operator() (const std::vector<int>& x, const std::vector<int>& y)
{
std::vector<int> result;
// Not strictly necessary, but helps with performance a bit
result.reserve(std::min(x.length(), y.length());
for (std::vector<int>::const_iterator x_it = x.begin(),
y_it = y.begin();
x_it != x.end() && y_it != y.end();
++x_it, ++y_it)
{
result.push_back(*x_it + *y_it);
}
return result;
}
It looks like you've already gotten a reasonable answer or two; I'll just point out an alternative. Though I hesitate to mention it, std::valarray fits so well for this I just can't resist:
std::valarray<int> x;
std::valarray<int> y;
// code to populate x and y elided
x += y;
Ever few months (or so) I see something valarray would make so simple I find it truly regrettable that it's been lost and forgotten (then I think about things like slice, gslice, slice_array, indirect_array, etc., and wish I hadn't thought of it at all).

C++ set search for pair element?

So I have a set of pairs<string ,string>
And I want to use find() to search for a single string which would be in the "first" of the pair, then if I find that string in first I want to return second from that function.
My current attempt is..
myList::iterator i;
i = theList.find(make_pair(realName, "*"));
return i->second;
Is C++11 acceptable?
auto it = find_if(theList.begin(), theList.end(),
[&](const pair<string, string>& val) -> bool {
return val.first == realName;
});
return it->second;
Or in C++03, first define a functor:
struct MatchFirst
{
MatchFirst(const string& realName) : realName(realName) {}
bool operator()(const pair<string, string>& val) {
return val.first == realName;
}
const string& realName;
};
then call it like so:
myList::iterator it = find_if(a.begin(), a.end(), MatchFirst(realName));
return it->second;
This will only return the first match, but from your question, it looks like that's all you're expecting.
You can use std::set<std::pair<std::string, std::string> > for this but you will need a custom
comparison object for this because the pair's relational operator takes both elements for this. That said, it seems as if you actually should use a std::map<std::string, std::string> instead.
The definition of < for std::pair implements a lexicographical order and "" is the minimum element for strings. Combining this we get:
typedef std::pair<std::string, std::string> StringPair;
typedef std::set<StringPair> Set;
std::string const* find_first(Set const& s, std::string const& key) {
Set::const_iterator const it = s.lower_bound(std::make_pair(key, ""));
// Check that it actually points to a valid element whose key is of interest.
if (it == s.end() or it->first != key) { return 0; }
// Yata!
return &it->second;
}
The trick is using lower_bound appropriately.
Returns an iterator pointing to the first element which does not compare less than value.
If it returns end(), then it did not find anything interesting.
Otherwise, it->first >= key so we get rid of the > case (of no interest to us)
I would point out though that this only returns the first element of the range. If you are interested in all elements, try:
typedef std::pair<Set::const_iterator, Set::const_iterator> SetItPair;
SetItPair equal_range_first(Set const& s, std::string const& key) {
StringPair const p = std::make_pair(key, "");
return std::make_pair(s.lower_bound(p), s.upper_bound(p));
}
This will return the full range of nodes in s whose first element is equal to key. You then just have to iterate over this range:
for (Set::const_iterator it = range.first; it != range.second; ++it) {
// do something
}
And you don't even have to worry whether the return of lower_bound or upper_bound was end or not.
if lower_bound returns end(), then so does upper_bound, and the loop is skipped
if lower_bound points to a node for which it->first > key, then upper_bound will point to that same node, and the loop is skipped
That is the power of ranges: no need to make special checks, the ranges just end up empty when there is no match, and so the loop over them... is skipped in a single check.