Issues in searching through the vector? - c++

I have a vector of the form given below (in C++):
vector<pair<int,int> > u;
Now when the first element of u.first becomes equal to 12 then I want to break from the loop. I am using the following code for this:
while(1){
if((find(u.begin().first, u.end().first, 12)!=u.end().first))
{
break;
}
}
However, it gives me the error that
'unable to resolve identifier first'

std::find iterates over a range and returns an iterator to the first element in the sequence that matches the provided value (12, in your case). The iterators are not the element in the container, they are pseudo-references to elements in the container.
You have to dereference an iterator to get the element. So, u.begin()->first would be the first value of the initial element of the container. u.begin().first is nonsensical.
In any case, to find a matching element using an operation other than ==, you need to use find_if with a custom predicate. For example, using a lambda expression:
auto const it(std::find_if(u.begin(), u.end(), [](std::pair<int, int> const& v)
{
return v.first == 12;
}));
if (it != u.end())
continue;

Related

How can I search for a element in unordered_set of pairs on the basis of first element>

unordered_set<pair<int, int> s;
I want to perform s.find() operation but I don't know how to implement it on the basis of first element.
eg, if my set is: (3,4),(2,5),(5,6),(7,8)
and I want to find 2nd element whose first element is 7 how can I do that.
std::find_if(s.begin(), s.end(), [](auto& el){ return el.first == 7; })
or with C++20:
std::ranges::find_if(s, [](auto& el){ return el.first == 7; })
(Both require #include<algorithm>.)
This will give back an iterator to an element containing 7 in the first position of the pair and s.end() if no such element was found.
However, this is cumbersome and has linear time complexity in the size of the container.
If you need to do this search often, you should probably use a
std::unordered_map<int, int>
or
std::unordered_multimap<int, int>
instead (depending on whether the first element may have the same value multiple times).
Then it is as easy as
s.find(7)

What does "result.second == false" mean in this code?

I came across this c++ code for counting frequency in a vector.
std::map<std::string, int> countMap;
// Iterate over the vector and store the frequency of each element in map
for (auto & elem : vecOfStrings)
{
auto result = countMap.insert(std::pair<std::string, int>(elem, 1));
if (result.second == false)
result.first->second++;
}
from https://thispointer.com/c-how-to-find-duplicates-in-a-vector/. I want to ask what does
result.second == false mean?
Since std::map and the other non-multi associative containers only store unique items there is a chance that when you insert something into it it wont actually insert since it may already be present. insert therefore returns a std::pair<iterator, bool> where the bool will be true if the insert succeeded and false otherwise.
I would like to point out you can get rid of the if statement in the loop. Because of how operator[] of a map works the loop can be replaced with
for (const auto & elem : vecOfStrings) // also added const here since we don't need to modify elem
{
++countMap[elem];
}
And now if elem exists then you increment the value and if it doesn't you added elem to the map and increment its value.
std::map::insert returns a std::pair<iterator, bool>.
pair.first is an iterator to the newly inserted element OR the element that was already in the map and prevented the insertion.
pair.second tells whether or not the insertion happened.
result.second == false is detecting the case where nothing was inserted into the map due to a key collision.
Note that with C++17, this can be written to be a bit more clear:
auto [itr, inserted] = countMap.insert({elem, 1});
if (!inserted) {
itr->second++;
}
From cppreference:
Returns a pair consisting of an iterator to the inserted element (or
to the element that prevented the insertion) and a bool denoting
whether the insertion took place.
result.first gives you the iterator to the element, while result.second tells you whether the element was actually inserted or did already exist.
std::map::insert returns a pair where the second value indicates whether any insertion actually happened. If the value is false, this means no value was inserted into the map because a value with the same key already exists.
However, the code shouldn’t be written like this: comparing against boolean literals is a nonsensical operation. Instead you’d write
if (not result.second)
// or
if (! result.second)
std::map::insert returns a pair of iterator and a bool. The bool indicates whether the insertion actually took place. The code you listed seems to increment the mapped int if key collision happens on insert.

How do I remove an element from a vector based on a condition?

I have a vector which I use for an observer pattern to register the name and pointer: a registration list.
I want to unregister an observers from the vector pair.
I am not sure how to proceed, I tried this but does not compile at all.
vector < pair<string , Observer* > > destination;
void subject::unregisterObservers(LogObserver* obsIfP)
{
vector <pair<string, LogObserverIf*> > ::iterator it;
for(it = observers.begin(); it != observers.end(); it++)
{
if((*it).second == obsIfP)
{
remove(observers.begin(), observers.end(), (*it).first);
break;
}
}
}
How do I remove elements from the vector based on one of the values inside a pair element?
You should use vector::erase() instead.
for(it = observers.begin(); it != observers.end(); it++)
{
if(it->second == obsIfP)
{
observers.erase(it);
break;
}
}
There's a few issues with your current code.
std::remove will find and move elements equal to the given value to the end of the container. It then returns the iterator pointing to the end of the non-removed range in the vector. To have them completely removed would require vector.erase with the iterator returned from the remove algorithm.
The erase-remove idiom:
v.erase( remove( begin(v), end(v), value ), end(v) )
Note, your code gives a string as value and will not compile, since elements in the vector are pair< string, Observer* > and the algorithm can't compare between the two.
Erasing while iterating over the same range is dangerous, since you may invalidate the iterators of the first loop.
Using algorithms with predicates:
Depending on the size of the vector, it may just be simpler (and even faster) to generate a new vector.
typedef pair<string, Observer*> RegPair;
vector<RegPair> new_observers;
new_observers.reserve( observers.size() ); // To avoid resizing during copy.
remove_copy_if( begin(observers), end(obervers), std::back_inserter(new_observers),
[obsIfP]( const RegPair& p ) -> bool
{ return p.second == obsIfP; } );
observers = std::move(new_observers);
// --- OR THIS
observers.erase( remove_if( begin(observers), end(observers),
[obsIfP]( const RegPair& p ) -> bool
{ return p.second == obsIfP; } ),
end(observers) );
Removing an element in the middle of a vector will cause all the following elements to be moved back one index, which is essentially just a copy anyway. If more than one element has the observer pointer, your initial code would have to move these elements more than once, while this suggestion always has a worst case of O(N), where the elementary operation is a copy of the pair element. If better performance than O(N) is required, you'll have to arrange your observers with std::map< string, Observer* > or perhaps boost::bimap which lets you use both pair values as keys.
The difference between remove_if and copy_remove_if would be the preserving of order. remove_if may swap elements and place them elsewhere in order to get the removed element to the end-range. The copy_remove_if will simply not copy it over to the new container, so order is preserved.
If you're not using c++11, the lambda wont work and you'll have to hand code the loop yourself.

find a pair in a STL list where only first element is known

assumend I have a (filled) list
std::list<std::pair<int,otherobject>> myList;
and want to find() the first element within this list, where int has a specific value - how can I do that?
To explain it a bit further:
I want to append these pairs to the list with an int that identifies otherobject but is not unique. The order where these int/otherobject pairs arrive has to be kept.
When an int is found during access to elements of this list the first occurence of that int has to be given back (and removed).
Thanks!
I think I'd use the standard find_if algorithm:
auto pos = std::find_if(myList.begin(), myList.end(),
[value](std::pair<int, otherobject> const &b) {
return b.first == value;
});
That gives an iterator to the element with the required value -- from there, you can copy the value, delete the value, etc., just like with any other iterator.
According to your need the better option would be to use a multimap.
In you case it would give :
std::multimap<int, otherobject> myMultiMap;
Then when looking for otherobjects linked to a int ( myInt) you'll do :
std::pair<std::multimap<int, otherobject>::iterator, std::multimap<int, otherobject>::iterator> result = myMultiMap.equal_range(myInt);
for (std::multimap<int,otherobject>::iterator iter=result.first; iter!=result.second; ++iter)
{
std::cout << it->second;
}
This is a STL container so you'll easilly find online documentation.

How to correctly handle an iterator for upper_bound

I have a C++ map, called tableMap, with is a std::map<int, std::string>.
void processMap(int key) {
std::map<int, std::string>::const_iterator iter;
while (true) {
// key is an argument to the function
iter = tableMap.upper_bound(key);
--iter;
std::string value = iter->second;
// do something else
...
}
}
The point is I'm not handling the upper_bound function call correctly. Also, I can't just check that
if (iter != tableMap.end())
because if the key is on the last element, then upper_bound would return end(). From the C++ API, we have:
Return iterator to upper bound
Returns an iterator pointing to the first element in the container whose key is considered to go after k.
I'm clearly not handling the corner cases, and that piece of code is buggy. What should I do in other to cover the corner cases? Should I replace upper_bound() by find() or lower_bound() ?
The goal is to to find the next element greater than key, so that's why I'm decreasing iter. The reason was some overlapping ranges in the map.
Program received signal SIGSEGV, Segmentation fault.
0x0000003d3ba69eea in std::_Rb_tree_decrement(std::_Rb_tree_node_base*) () from /usr/lib64/libstdc++.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.47.el6_2.5.x86_64 keyutils-libs-1.4-4.el6.x86_64 krb5-libs-1.8.2-3.el6_0.3.x86_64 libcom_err-1.41.12-3.el6.x86_64 libgcc-4.4.6-3.el6.x86_64 libibverbs-1.1.5mlnx1-1.32.gc42bcbf.x86_64 libselinux-2.0.94-2.el6.x86_64 libstdc++-4.4.6-3.el6.x86_64 openssl-1.0.0-4.el6_0.2.x86_64 pcre-7.8-3.1.el6.x86_64 zlib-1.2.3-27.el6.x86_64
(gdb) bt
#0 0x0000003d3ba69eea in std::_Rb_tree_decrement(std::_Rb_tree_node_base*) () from /usr/lib64/libstdc++.so.6
#1 0x0000000000da8a41 in std::_Rb_tree_const_iterator<int, std::string >::operator-- (this=0x7fffffffb8b0)
at /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_tree.h:274
#2 0x0000000000de18db in processMap (
this=0x17107b8,key=0)
Compare these two:
The goal is to to find the next element greater than key, so that's why I'm decreasing iter. The reason was some overlapping ranges in the map.
Returns an iterator pointing to the first element in the container whose key is considered to go after k
That means, upper_bound already does what you need. So don't decrement the iterator. There are three corner cases:
The map is empty, then there is only one iterator, namely end()/begin(). Decrement, increment and dereferencing of iter wil give UB.
The map contains no value with a key greater than your argument. upper_bound will return end(), so don't dereference it.
The map contains only values with keys greater than your argument. upper_bound will return begin(). Your decrement is UB then, but since it's wrong anyways, you can just use it and dereference it.
So you only have to handle the first two cases, where upper_bound returns end():
void processMap(int key) {
while (true) {
auto iter = tableMap.upper_bound(key);
if (iter == tableMap.end())
break; //there is no entry with a greater key
// use iter...
}
}