C++ std::find_if on iterator in reverse order - c++

In the following code, is there an elegant way to find it_end?
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
#include <type_traits>
template <class Iterator, class U = typename std::iterator_traits<Iterator>::value_type>
void process(Iterator begin, Iterator end)
{
// first nonzero
auto it_begin = std::find_if(begin, end, [](U val){return val != 0;});
// end of nonzero values
int n = std::distance(begin, end);
int index = n-1;
for(int i=n-1; i>=0; --i) {
if(*(begin+i) == 0) {
index = i;
} else {
break;
}
}
auto it_end = begin;
std::advance(it_end, index);
// ******* is there a way to use std::find_if or similar function to get it_end? *******
for(auto it=it_begin; it!=it_end; ++it) {
// a whole bunch of things
// ...
std::cout << *it << std::endl;
}
}
int main()
{
std::vector<int> v{0,0,1,2,3,0,0};
process(v.begin(), v.end());
}

use std::reverse_iterator.
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
#include <type_traits>
#include <list>
template <class Iterator, class U = typename std::iterator_traits<Iterator>::value_type>
void process(Iterator begin, Iterator end)
{
// first nonzero
auto it_begin = std::find_if(begin, end, [](U val){return val != 0;});
if (it_begin == end)
return; // all value is zeros.
// reverser iterator
auto r_begin = std::reverse_iterator(end);
auto r_end = std::reverse_iterator(it_begin);
auto r_find = std::find_if(r_begin, r_end, [](U val) { return val != 0; });
auto it_end = r_find.base();
for(auto it = it_begin; it != it_end; ++it) {
std::cout << *it << std::endl;
}
}
int main()
{
std::list<int> v{0,0,0,2,0,3,4};
process(v.begin(), v.end());
}

You can use the reverse iterator.
My implementation:
template <class Iterator, class U = typename std::iterator_traits<Iterator>::value_type>
void yet_another_process(Iterator begin, Iterator end, std::reverse_iterator<Iterator> rbegin, std::reverse_iterator<Iterator> rend) {
auto it_begin = std::find_if(begin, end, [](U val) { return val != 0; });
auto it_end = std::find_if(rbegin, rend, [](U val) { return val != 0; }).base(); // Notice this base()
for (auto it = it_begin; it != it_end; ++it) {
std::cout << *it << std::endl;
}
}
and in main():
int main() {
std::vector<int> v { 0, 0, 1, 2, 3, 0, 0 };
yet_another_process(v.begin(), v.end(), v.rbegin(), v.rend());
}
The base() method of a reverse iterator returns the underlying "normal" iterator.

Related

Is there a way to convert local_iterator to const_iterator or iterator?

How can we change unordered_multimap::local_iterator to unordered_multimap::iterator or unordered_multimap::const_iterator?
I need the change because we cannot erase elements using local_iterator, and erasing can only be done using iterator/const_iterator. If there is any other way of erasing using local_iterator, please do suggest.
Kinda labor intensive, but you can iterate through the result of equal_range() until you find the correct iterator:
template<typename Cont>
typename Cont::iterator local_to_regular_iterator(Cont& c, typename Cont::local_iterator local_ite) {
auto range = c.equal_range(local_ite->first);
for(auto ite = range.first; ite != range.second; ++ite) {
if(&ite->second == &local_ite->second) {
return ite;
}
}
throw std::out_of_range("huh?");
}
As far as I can tell, that's as good as you can get currently.
You could use find_if algorithm to search for an element with the same address like so:
auto it = std::ranges::find_if(m,
[ptr = std::addressof(*local_it)](const auto& e) -> bool
{
return ptr == std::addressof(e);
}
);
Here is a full code snippet:
#include <string_view>
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <ranges>
#include <memory>
int main()
{
std::unordered_multimap<int, char> m;
m.insert({1, 'a'});
m.insert({1, 'b'});
m.insert({2, 'c'});
auto local_it = m.begin(m.bucket(1));
std::cout << local_it->first << local_it->second << '\n';
auto it = std::ranges::find_if(m,
[ptr = std::addressof(*local_it)](const auto& e) -> bool
{
return ptr == std::addressof(e);
}
);
std::cout << it->first << it->second << '\n';
return 0;
}
Run it here.

How to look for an element inside a set of sets in C++?

Following is my code:
set<set<int,greater<int>>> output;
set<int,greater<int>> k;
k.insert(5);
output.insert(k);
Now how do I find out if element 5 is present in my 'output' set or not?
And how do I find out which set, inside my 'output' set, the element 5 belongs to?
Use a simple for loop:
size_t ii = 0;
for (const auto& inner : output) {
if (inner.count(5))
std::cout << "found in set " << ii << std::endl;
++ii;
}
There's a pre C++20 and a C++20 (using ranges) solution
Pre-C++20 I'm using std::find_if and set::find.
C++20 I'm using std::ranges::find_if and set::contains.
For the C++20 I'm also using std::optional std::reference_wrapper return type. And concepts, so it's a generic solution applicable to all sets and maps.
#include <set>
#include <algorithm>
template<typename T, typename Comp = std::less<T>>
std::set<T, Comp> const* findInSetOfSets(std::set<std::set<T, Comp>> const& setOfSets, T const& value) {
auto const it = std::find_if(cbegin(setOfSets), cend(setOfSets),
[&](std::set<T, Comp> const& set) { return set.find(value) != end(set); } );
if (it != cend(setOfSets)) return &*it;
return nullptr;
}
#include <optional>
#include <functional>
#include <type_traits>
template<typename T, typename U>
concept hasContains = requires(T& t, U& u) {
{ t.contains(u) } -> std::same_as<bool>;
};
template<std::ranges::input_range R, typename T,
typename U = std::remove_reference<R>::type::value_type>
requires hasContains<U, T>
[[nodiscard]] auto findCpp20(R&& r, T const& value) noexcept
->std::optional<std::reference_wrapper<U const>> {
auto it = std::ranges::find_if(r,
[&](U const& u) { return u.contains(value); } );
if (it != std::ranges::end(r)) return *it;
return std::nullopt;
}
#include <cstdio>
int main(){
std::set<std::set<int,std::greater<int>>> output;
std::set<int,std::greater<int>> k;
k.insert(5);
output.insert(k);
auto setPtr = findInSetOfSets(output, 5);
if (setPtr != nullptr) printf("set found!\n");
auto optionalSet = findCpp20(output, 5);
if (optionalSet.has_value()) printf("set found!\n");
}
godbolt
Here's a boolean function that looks for element p inside a set of sets s
bool findelem(int p, set<set<int>> const& s)
{
for (auto itr1= s.cbegin(); itr1 != s.cend(); ++itr1) {
for (auto itr2 = itr1->cbegin(); itr2 != itr1->cend(); ++itr2) {
if (*itr2==p) {
return true;
}
}
}
return false;
}

remove duplicates from sorted array, same solution with different code way has different output

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "hello_world.h"
using namespace std;
class Solution1 {
public:
int removeDuplicates(vector<int>& nums) {
return distance(nums.begin(), removeDuplicates(nums.begin(), nums.end(), nums.begin()));
}
template<typename InIt, typename OutIt>
OutIt removeDuplicates(InIt begin, InIt end, OutIt output){
while(begin != end){
*output++ = *begin;
begin = upper_bound(begin, end, *begin);
}
return output;
}
};
class Solution2 {
public:
int removeDuplicates(vector<int>& nums) {
vector<int>::iterator output = nums.begin();
while(nums.begin() != nums.end()){
*output++ = *nums.begin();
nums.begin() = upper_bound(nums.begin(), nums.end(), *nums.begin());
}
return distance(nums.begin(), output);
}
};
int main()
{
//helloworld test;
//test.print();
int num[3] = {1,1,2};
vector<int> nums(num, num + 3);
Solution2 so;
int a = so.removeDuplicates(nums);
cout<<a<<endl;
return 0;
}
In main function, when i use the class solution1, the code can remove duplicates numbers from the arrary [1 1 2] ,to output [1 2]. In order to simplify the code, I changed the solution1 to solution2, but the solution2 can not execute right output, anybody know the reason?
In this while loop
while(nums.begin() != nums.end()){
*output++ = *nums.begin();
nums.begin() = upper_bound(nums.begin(), nums.end(), *nums.begin());
}
you are always using the iterator nums.begin() in the condition and in this statement
*output++ = *nums.begin();
because this statement
nums.begin() = upper_bound(nums.begin(), nums.end(), *nums.begin());
does not change the iterator returned by a new call of nums.begin().
You need to introduce a variable of the iterator type before the loop like
auto it = nums.begin();
while( it != nums.end()){
*output++ = *it;
it = upper_bound( it, nums.end(), *it );
}
Here is a demonstrative program
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<int> v = { 1, 2 };
size_t i = 0;
while ( v.begin() != v.end() )
{
v.begin() = std::upper_bound( v.begin(), v.end(), *v.begin() );
if ( ++i == 10 ) break;
}
std::cout << "i = " << i << '\n';
i = 0;
auto it = v.begin();
while ( it != v.end() )
{
it = std::upper_bound( it, v.end(), *it );
if ( ++i == 10 ) break;
}
std::cout << "i = " << i << '\n';
return 0;
}
Its output is
i = 10
i = 2
To erase duplicates after the loop use the member function erase like
nums.erase( output, nums.end() );
The same can be done using the standard algorithm std::unique. For example
nums.erase( std::unique( nums.begin(), nums.end() ), nums.end() );

Iterating over a container bidirectionally

Is there a better way than the below code, to iterate over a container in either direction, using the same iterators?
#include <iostream>
#include <map>
int main()
{
const bool descend = false;
std::map<int, int> mapp;
mapp[1] = 1;
mapp[2] = 2;
mapp[3] = 3;
mapp[4] = 4;
std::map<int, int>::iterator startIter = descend ? --(mapp.end()) : mapp.begin();
std::map<int, int>::iterator endIter = descend ? --(mapp.begin()) : mapp.end();
while (startIter != endIter)
{
std::cout << startIter->first << std::endl;
descend ? --startIter : ++startIter;
}
}
Your code is invalid as this statement --(mapp.begin()) leads to UB. I would write a thin wrapper:
template<class Iter, class F>
void apply( Iter begin, Iter end, F f, bool forward )
{
while( begin != end )
f( forward ? *begin++ : *--end );
}
live example
or just simply rewrite your loop into:
auto begin = mapp.begin();
auto end = mapp.end();
while ( begin != end)
{
const auto &p = forward ? *begin++ : *--end;
std::cout << p.first << std::endl;
}
Is there a better way than the below code, to iterate over a container
in either direction, using the same iterators?
Yes. Use std::map::reverse_iterator. It will be a better way than the code you posted, but that will not be using the same iterators anymore, which was one of your requirements.
However, this will be less error-prone than the code you have written. In addition to that, you do not need to re-invent the wheel, if that is already in C++.
See output here
#include <iostream>
#include <map>
template<typename Iterator>
void print(const Iterator Begin, const Iterator End)
{
for(Iterator iter = Begin; iter != End; ++iter)
std::cout << iter->first << "\n";
}
int main()
{
const bool descend = true;
std::map<int, int> mapp;
mapp[1] = 1;
mapp[2] = 2;
mapp[3] = 3;
mapp[4] = 4;
descend ?
print(mapp.crbegin(), mapp.crend()):
print(mapp.cbegin(), mapp.cend());
return 0;
}
The image from cppreference.com will explain graphically, how does it work.
Write self-documenting code and it becomes simple. Break that loop out into its own function and call it with the appropriate iterators.
This is why we have "reverse iterators" they can be used to go backwards through a container by using the normal forward semantics.
#include <iostream>
#include <map>
template<typename I>
void printMapContainer(I begin, I end)
{
for (;begin != end; ++begin)
{
std::cout << begin->first << "\n";
}
}
int main()
{
const bool descend = false;
std::map<int, int> mapp;
mapp[1] = 1;
mapp[2] = 2;
mapp[3] = 3;
mapp[4] = 4;
if (descend) {
printMapContainer(mapp.rbegin(), mapp.rend());
}
else {
printMapContainer(mapp.begin(), mapp.end());
}
}

Cleaning up bidirectional iterator code

I've modified James' flattening iterator to act as a bidirectional iterator if possible, but I don't think my changes are very elegant (particularly relying on a bool to see if the inner iterator has been set). However, I can't seem to come up with a nicer solution. Does anyone have any ideas?
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
#include <iterator>
#include <type_traits>
// An iterator that "flattens" a container of containers. For example,
// a vector<vector<int>> containing { { 1, 2, 3 }, { 4, 5, 6 } } is iterated as
// a single range, { 1, 2, 3, 4, 5, 6 }.
template <typename OuterIterator>
class flattening_iterator
{
public:
typedef OuterIterator outer_iterator;
typedef typename std::iterator_traits<outer_iterator>::value_type::iterator inner_iterator;
typedef typename std::iterator_traits<outer_iterator>::iterator_category outer_category;
typedef typename std::iterator_traits<inner_iterator>::iterator_category inner_category;
typedef typename std::common_type<outer_category, inner_category>::type common_category;
typedef typename std::conditional<std::is_same<common_category, std::random_access_iterator_tag>::value,
std::bidirectional_iterator_tag,
common_category>::type iterator_category;
typedef typename std::iterator_traits<inner_iterator>::value_type value_type;
typedef typename std::iterator_traits<inner_iterator>::difference_type difference_type;
typedef typename std::iterator_traits<inner_iterator>::pointer pointer;
typedef typename std::iterator_traits<inner_iterator>::reference reference;
flattening_iterator() { }
flattening_iterator(outer_iterator it, outer_iterator begin, outer_iterator end)
: outer_it_(it),
outer_begin_(begin),
outer_end_(end),
inner_it_assigned_(false)
{
if (outer_begin_ == outer_end_) { return; }
if (outer_it_ == outer_end_) { return; }
inner_it_ = outer_it_->begin();
inner_it_assigned_ = true;
advance_past_empty_inner_containers();
}
reference operator*() const { return *inner_it_; }
pointer operator->() const { return &*inner_it_; }
flattening_iterator& operator++()
{
++inner_it_;
if (inner_it_ == outer_it_->end())
advance_past_empty_inner_containers();
return *this;
}
flattening_iterator operator++(int)
{
flattening_iterator it(*this);
++*this;
return it;
}
flattening_iterator& operator--()
{
if(!inner_it_assigned_)
{
if(outer_begin_ != outer_end_)
{
decrement_through_empty_inner_containers();
}
return *this;
}
if(inner_it_ == outer_it_->begin())
{
decrement_through_empty_inner_containers();
}
else
{
--inner_it_;
}
return *this;
}
flattening_iterator operator--(int)
{
flattening_iterator it(*this);
--*this;
return it;
}
friend bool operator==(const flattening_iterator& a,
const flattening_iterator& b)
{
if (a.outer_it_ != b.outer_it_)
return false;
if(a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_assigned_ == false &&
b.inner_it_assigned_ == false)
return true;
if (a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_ != b.inner_it_)
return false;
return true;
}
friend bool operator!=(const flattening_iterator& a,
const flattening_iterator& b)
{
return !(a == b);
}
private:
void advance_past_empty_inner_containers()
{
while (outer_it_ != outer_end_ && inner_it_ == outer_it_->end())
{
++outer_it_;
if (outer_it_ != outer_end_)
inner_it_ = outer_it_->begin();
}
}
void decrement_through_empty_inner_containers()
{
--outer_it_;
while(outer_it_ != outer_begin_ && outer_it_->begin() == outer_it_->end())
{
--outer_it_;
}
if(outer_it_->begin() != outer_it_->end())
{
inner_it_ = --outer_it_->end();
inner_it_assigned_ = true;
}
}
outer_iterator outer_it_;
outer_iterator outer_begin_;
outer_iterator outer_end_;
inner_iterator inner_it_;
bool inner_it_assigned_;
};
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator start, Iterator first, Iterator last)
{
return flattening_iterator<Iterator>(start, first, last);
}
template <typename Iterator>
std::reverse_iterator<flattening_iterator<Iterator>> flatten_reverse(Iterator start, Iterator first, Iterator last)
{
return std::reverse_iterator<flattening_iterator<Iterator>>(flatten(start, first, last));
}
int main()
{
std::vector<std::vector<int>> v(3);
int i(0);
for (auto it(v.begin()); it != v.end(); ++it)
{
it->push_back(i++); it->push_back(i++);
it->push_back(i++); it->push_back(i++);
}
v.insert(v.begin(), std::vector<int>());
v.insert(v.begin(), std::vector<int>());
v.insert(v.begin() + 4, std::vector<int>());
v.push_back(std::vector<int>());
v.push_back(std::vector<int>());
for (auto it(flatten(v.begin(), v.begin(), v.end())), end = flatten(v.end(), v.begin(), v.end());
it != end;
++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
for (auto it(flatten_reverse(v.end(), v.begin(), v.end())), end = flatten_reverse(v.begin(), v.begin(), v.end());
it != end;
++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
std::vector<std::vector<int>> v2;
for (auto it(flatten(v2.end(), v2.begin(), v2.end())), end = flatten(v2.begin(), v2.begin(), v2.end());
it != end;
--it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
}
Great question, and great attempt.
An iterator should always refer to a valid value, or one-past-the-end. *iter should always be valid unless iter == end where end is one-past-the-end. That "one-past-the-end" iterator is the cause of your worries. Either inner_it_ refers to a valid value, or your iterator is one-past-the-end.
James's "one-past-the-end" iterator exists when outer_it_ == outer_end_, and this is the situation you need to check for. This is the only situation in which inner_it_ should have an invalid value. As a result, you can get rid of the bool and just directly check outer_it_ == outer_end_.
Also, I find this line suspect:
inner_it_ = --outer_it_->end();
Is it possible that outer_it_ is a typedef to a pointer? If so, you can't call -- on a pointer value. This will definitely work:
inner_it_ = outer_it_->end();
--inner_it_;
and moreover, it conveys intent better, because the first looks like you're decrementing the end() iterator itself!