In C++, I am aware that you can have multiple iterators. For example, if you had a function that wanted to see the first concurrence of a string in two iterators why would a code look something like this:
Iterator found_it(Iterator one, Iterator two){
while(one != two && (*one) != "Bob"){
one++;
}
return one;
}
*The question said one and two don't necessarily mean begin() and end() - that is what really gave me a mind**** and confusion :S*
Firstly, what happens if Bob was in iterator two? Because you are only returning iterator one? This is what is really confusing me at the moment.
Thanks
When two iterators used as a range for a function or a standard algorithm then the second iterator is not included in the range. That is you should consider the range like
[first, last )
If a function or algorithm like std::find returns the second iterator then it means that the range does not contain the target value.
If the second iterator was included in the range then a question arises what iterator to return when the target value is not found?
Consider for example your own function with the following its calls
template <class Iterator>
Iterator found_it(Iterator one, Iterator two){
while(one != two && (*one) != "Bob"){
one++;
}
return one;
}
//...
std::vector<std::string> v1 = { "Mary", "Bob" };
std::vector<std::string> v2 = { "Mary", "Peter" };
auto it1 = found_it( v1.begin(), v1.end() );
if ( it1 != v1.end() ) std::cout << *it1 << " is present in v1" << std::endl;
else std::cout << "Bob" << " is not present in v1" << std::endl;
auto it2 = found_it( v2.begin(), v2.end() );
if ( it2 != v2.end() ) std::cout << *it2 << " is present in v2" << std::endl;
else std::cout << "Bob" << " is not present in v2" << std::endl;
one and two are the beginning and end (really just beyond the end) of the range that you want to search. This might not be the same as begin() and end() on a container, if you wanted to search only a subrange, or if the iterators don't come from a container at all.
Note that one is changed within the loop; you're not necessarily returning the original value of one.
Your function could be rewritten equivalently like this:
Iterator found_it(Iterator begin, Iterator end) {
Iterator current = begin;
while (current != end && (*current) != "Bob"){
current++;
}
return current;
}
If "Bob" is at iterator two then it is out of the range you wish to search so it should not be found. The second iterator is one past the search range.
So you search from one up to, but not including, two.
When you want to search an entire container then you will pass begin() and end() as arguments to one and two. But iterators give you the flexibility to search for intermediate ranges within a container.
For example what if you want to find all the Bobs?
std::vector<std::string> names {"Tim", "Beryl", "Bob", "Danny", "Bob", "Lou"};
You can get the first one like this:
auto bob1 = fond_it(names.begin(), names.end());
You can get the second one like this:
auto bob2 = fond_it(bob1 + 1, names.end());
Notice you can start the search half way through using bob1 rather than at the beginning.
Related
I am hoping you can help me out here. I have searched for other answers, but I havent found something that matches my specific situation (but if you do find one, please let me know the URL!). I have seen a lot of suggestions about using std::map instead of list and I dont mind switching the container if need be.
Currently, I have two Lists of pairs i.e.
std:list <std::pair<string,string>> outputList1;
std:list <std::pair<string,string>> outputList2;
I have populated each list with User Settings that I have retrieved from an SQL database (I omit the SQL retrieval code here).
Example list:
outputList1 (first, second)
CanSeeAll, True
CanSubmit, False
CanControl, False
OutputList2:
CanSeeAll, False
CanSubmit, True
CanControl, False
I want to iterate through both lists and find the mismatches. For example, find the first string of the first pair of the first list to find the matching first string in the second list, then compare the second string to determine whether they match, then print out the non matching pairs to a new string (eventually to file), and so on.
In this example, the final string would have CanSeeAll and CanSubmit as the final output since those are the two that mismatch.
Here is what I've tried so far, but I get a blank string:
std::list <std::pair<std::string,std::string>>::iterator it1 = outputList1.begin();
std::list <std::pair<std::string,std::string>>::iterator it2 = outputList2.begin();
string token;
while (it1 != outputList1.end()){
if((*it1).first == ((*it2).first))
{
if((*it1).second != ((*it2).second))
{
token.append((*it1).first);
token.append(",");
token.append((*it1).second);
token.append("\r\n");
}
it1++;
it2 = outputList2.begin();
}
it2++;
if (it2 == outputList2.end())
it1++;
}
I know this logic is flawed as it will skip the first pair on the second list after the first iteration, but this is the best I can come up with at the moment, and I am banging my head on the keyboard a the moment.
Thanks everyone!
As I understand the problem,
you want to compare every element of one list, to every other element of another list.
You could use a pair of nested range based for loops.
#include <list>
#include <string>
int main(){
std::list<std::pair<std::string,std::string>> l1;
std::list<std::pair<std::string,std::string>> l2;
for (auto x: l1){
for (auto y: l2){
//compare x to y
}
}
}
The answer uses an auxiliary map but, have in mind you will get better result if you use two maps (or hash tables) instead of two list.
// create a map for elements in l2
std::map<std::string, std::string> l2map;
// move elements from l2 to the map so we get O(N*log(N)) instead of O(n²)
for (std::list<std::pair<std::string,std::string> >::iterator it = l2.begin();
it != l2.end();
++it)
{
l2map.insert(*it);
}
// walk l1 and look in l2map
for (std::list<std::pair<std::string,std::string> >::iterator l1it = l1.begin();
l1it != l1.end();
++l1it)
{
// look for the element with the same key in l2
// l1it->first is the key form l1
std::map<std::string, std::string>::iterator l2it = l2map.find(l1it->first);
if (l2it != l2map.end()) {
// found, then compare
if (l1it->second != l2it->second) { // l1it->second is the value from l1
// mismatch
}
} else {
// not in l2
}
}
You could use std::mismatch with the pre-condition: all settings occur in the same order in both lists (you could do a sort if this is not the case)
auto iterPair = std::mismatch(l1.begin(), l1.end(), l2.begin());
while (iterPair.first != l1.end()) {
// TODO: Handle the mismatching iterators
iterPair = std::mismatch(iterPair.first + 1, l1.end(), iterPair.second + 1);
}
If the keys in your lists come in the same order, as in your example, you can traverse the lists linearly:
std::ostringstream s;
std:list<std::pair<string, string>>::const_iterator i2(outputList2.cbegin());
for(auto const &pair: outputList1) {
if(pair.second != i2->second) {
s << pair.first << ": " << pair.second << " != " << i2->second << endl;
}
++i2;
}
Alternatively, use STL algorithms:
#include <algorithm>
typedef std::list<std::pair<std::string, std::string>> List;
std::ostringstream s;
for(
auto itrs(
std::mismatch(
outputList1.cbegin(), outputList1.cend(), outputList2.cbegin()
, [](auto const &l, auto const &r){ return l.second == r.second; }))
; itrs.first != outputList1.cend()
; itrs = std::mismatch(itrs.first, outputList1.cend(), itrs.second
, [](auto const &l, auto const &r){ return l.second == r.second; }))
{
s << itrs.first->first << ": "
<< itrs.first->second << " != " << itrs.second->second
<< std::endl;
}
This is not a duplicate of how to convert a reverse_iterator to an iterator because I want the result to be different to the normal conversion.
Given only a reverse_iterator that is returned from rend, is it possible to convert this to the corresponding iterator that would be returned from end?
For example
std::vector<int> myvec = {...};
auto rit = myvec.rend();
auto it = MAGIC(rit);
it == myvec.end(); // true
If it is not possible to do this given only the reverse_iterator, what is the minimum information needed to do this? (So I can consider workarounds).
Short answer: No.
An iterator refers to one single point in a container, wihtout actual knowledge of the container itself. The iterators returned by end() and rend() point to different ends of a container, i.e. there might be some, many or no elements between the points they refer to, regardless of the reverse nature of one of the iterators. So, without knowing about the container itself or at least its size, there is no possibility to get from one end of the container to the other, and since iterators don't have that knowledge, there is no possibility to get from rend() to end(), from end() to begin() etc. without additional information.
The minimum needed information is the size of the "gap" between the two points. With that and the normal conversion between reverse and non-reverse iterators it is an easy task:
auto rend = v.rend();
auto begin = rend.base();
assert(begin == v.begin());
auto end = begin + v.size(); //the size is the key!
assert(end == v.end());
But, since you can not obtain the size from the reverse_iterator but only from the container itself, you can easily ask it for end() in the first place.
Expression
myvec.rend().base()
is equivalent to
myvec.begin()
Here is a demonstrative example
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<int>::iterator it = v.rend().base();
std::cout << *it << std::endl;
return 0;
}
The output is
1
Another demonstrative program that shows the relation between std::vector<int>::iterator and std::vector<int>::reverse_iterator (instead of templetae argument int you may use any type T>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
if ( v.begin() == v.rend().base() )
{
std::cout << "v.begin() == v.rend().base()" << std::endl;
}
if ( v.end() == v.rbegin().base() )
{
std::cout << "v.end() == v.rbegin().base()" << std::endl;
}
return 0;
}
The output is
v.begin() == v.rend().base()
v.end() == v.rbegin().base()
I ran into the following problem using std::multimap::equal_range() and insert().
According to both cplusplus.com and cppreference.com, std::multimap::insert does not invalidate any iterators, and yet the following code causes an infinite loop:
#include <iostream>
#include <map>
#include <string>
int main(int argc, char* argv[])
{
std::multimap<std::string,int> testMap;
testMap.insert(std::pair<std::string,int>("a", 1));
testMap.insert(std::pair<std::string,int>("a", 2));
testMap.insert(std::pair<std::string,int>("a", 3));
auto range = testMap.equal_range(std::string("a"));
for (auto it = range.first; it != range.second; ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
// this loop becomes infinite
}
// never gets here
for (auto it = testMap.begin(); it != testMap.end(); ++it)
{
std::cout << it->first << " - " << it->second << std::endl;
}
return 0;
}
The intent is to take all existing items in the multimap with a particular key ("a" in this case) and duplicate them under a second key ("b"). In practice, what happens is that the first loop never exits, because it never ends up matching range.second. After the third element in the map is processed, ++it leaves the iterator pointing at the first of the newly inserted items.
I've tried this with VS2012, Clang, and GCC and the same thing seems to happen in all compilers, so I assume it's "correct". Am I reading too much into the statement "No iterators or references are invalidated."? Does end() not count as an iterator in this case?
multimap::equal_range returns a pair whose second element in this case is an iterator to the past-the-end element ("which is the past-the-end value for the container" [container.requirements.general]/6).
I'll rewrite the code a bit to point something out:
auto iBeg = testMap.begin();
auto iEnd = testMap.end();
for(auto i = iBeg; i != iEnd; ++i)
{
testMap.insert( std::make_pair("b", i->second) );
}
Here, iEnd contains a past-the-end iterator. The call to multimap::insert doesn't invalidate this iterator; it stays a valid past-the-end iterator. Therefore the loop is equivalent to:
for(auto i = iBeg; i != testMap.end(); ++i)
Which is of course an infinite loop if you keep adding elements.
The end-iterator range.second is not invalidated.
The reason that the loop is infinite, is that each repetition of the loop body:
inserts a new element at the end of the map, thus increasing the distance between it and the end by one (so, after this insert, range no longer represents the equal_range for the key "a" because you have inserted a new key within the range it does represent, from the first "a" to the end of the container).
increments it, reducing the distance between it and the end by one.
Hence, it never reaches the end.
Here's how I might write the loop you want:
for (auto it = testMap.lower_bound("a"); it != testMap.end() && it->first == "a"; ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
}
A solution to make it work as expected (feel free to improve, it's a community wiki)
auto range = testMap.equal_range(std::string("a"));
if(range.first != range.second)
{
--range.second;
for (auto it = range.first; it != std::next(range.second); ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
}
}
This is probably really simple, but I can't find a simple example for it.
I understand that with a hash_multimap you can have several values mapped to a single key. But how exactly would I access those values. All the examples I stumbled across always just access the first value mapped to the the key. Heres an example of what I mean
key : value
1 : obj1a;
2 : obj2a, obj2b, obj2c
how would I access obj2b and obj2c, not just obj2a
The usual multimap iteration loop is like this:
#include <unordered_multimap>
typedef std::unordered_multimap<K, V> mmap_t;
mmap_t m;
for (mmap_t::const_iterator it1 = m.begin(), it2 = it1, end = m.end(); it1 != end; it1 = it2)
{
// outer loop over unique keys
for ( ; it1->first == it2->first; ++it2)
{
// inner loop, all keys equal to it1->first
}
}
To iterate over just one key value, use equal_range instead.
std::pair<mmap_t::const_iterator, mmap_t::const_iterator> p = m.equal_range(key);
for (mmap_t::const_iterator it = p.first; it != p.second; ++it)
{
// use "it->second"
}
For example, equal_range returns an two iterators, to the begin and end of the matching range :
void lookup(const map_type& Map, int key)
{
cout << key << ": ";
pair<map_type::const_iterator, map_type::const_iterator> p =
Map.equal_range(key);
for (map_type::const_iterator i = p.first; i != p.second; ++i)
cout << (*i).second << " ";
cout << endl;
}
where we're using a map_type like
class ObjectT; // This is the type of object you want to store
typedef hash_multimap<int, ObjectT> map_type;
Just grab an iterator to the first one and increment it. If the keys are still equal, you've got another entry with the same key value. You can also use equal_range.
I have a stl set of integers and I would like to iterate through all unique pairs of integer values, where by uniqueness I consider val1,val2 and val2,val1 to be the same and I should only see that combination once.
I have written this in python where I use the index of a list (clusters):
for i in range(len(clusters) - 1):
for j in range(i+1,len(clusters)):
#Do something with clusters[i],clusters[j])
but without an index I am not sure how I can achieve the same thing with a stl set and iterators. I tried out:
for (set<int>::iterator itr = myset.begin(); itr != myset.end()-1; ++itr) {
cout << *itr;
}
but this fails as an iterator doesn't have a - operator.
How can I achieve this, or must I use a different container?
How about something along the following lines:
for(set<int>::const_iterator iter1 = myset.begin(); iter1 != myset.end(); ++iter1) {
for(set<int>::const_iterator iter2 = iter1; ++iter2 != myset.end();) {
{
std::cout << *iter1 << " " << *iter2 << "\n";
}
}
This yields all N*(N-1)/2 unique pairs, where N is the number of integers in your set.
As an aside: use a const_iterator whenever you iterate over a container without modifying anything, it's good style and might have better performance.
EDIT: Modified the code to reflect the suggestion made by Steve Jessop.
You don't need to do end() - 1 since end() is an iterator that points after the last element in the container.
The corrected code is:
for (set<int>::iterator itr = myset.begin(); itr != myset.end(); ++itr) {
for (set<int>::iterator itr2 = itr + 1; itr2 != myset.end(); ++itr2) {
// Do whatever you want with itr and itr2
}
}
Put your data in a boost::bimap, then iterate it both ways, copying the results into a standard STL map which will enforce uniqueness.