Given the minimal C++11 STL example:
set<int> S = {1,2,3,4};
for(auto &x: S) {
cout << x;
cout << ",";
}
Is there a way to check if x is the one right before the end? The goal in this example is to output 1,2,3,4 and not the final comma at the end. Currently I use a standard for loop with two iterators,
set<int>::const_iterator itr;
set<int>::const_iterator penultimate_end_itr = --S.end();
for(itr=S.begin(); itr!=penultimate_end_itr;++itr)
cout << (*itr) << ',';
cout << (*penultimate_end_itr);
Which works, but is terribly cumbersome. Is there a way to do the check within the range-based for loop?
EDIT: The point of the question is not to print out a comma separated list. I want to know if a range-based for loop has any knowledge of the penultimate element in the list (i.e. is it one before the end). The minimal example was presented so we all have a common code block to talk about.
The very purpose of range-based for loops is to forget the iterator. As such, they only allow you access to the current value and not the iterator. Would the following code do it for you?
set<int> S = {1,2,3,4};
std::string output;
for(auto &x: S) {
if (!output.empty())
output += ",";
output += to_string(x);
}
cout << output;
EDIT
Another solution: Instead of comparing iterators (as one would do with "normal" for loops), you could compare the addresses of the values:
set<int> S = {1,2,3,4};
auto &last = *(--S.end());
for (auto &x : S)
{
cout << x;
if (&x != &last)
cout << ",";
}
Boost.Range can help out here:
if (std::begin(S) != std::end(S)) {
std::cout << *std::begin(S);
for (const auto &x: boost::make_iterator_range(std::next(std::begin(S)), std::end(S))) {
std::cout << ", " << x;
}
}
A much more flexible approach is to index the range, using boost::adaptors::indexed (since Boost 1.56):
for (const auto &element: boost::adaptors::index(S)) {
std::cout << (element.index() ? ", " : "") << element.value();
}
In versions of Boost prior to 1.56 boost::adaptors::indexed won't work but you can easily write a work-alike:
template <typename... T>
auto zip(const T&... ranges) -> boost::iterator_range<boost::zip_iterator<decltype(boost::make_tuple(std::begin(ranges)...))>>
{
auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(ranges)...));
auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(ranges)...));
return boost::make_iterator_range(zip_begin, zip_end);
}
template<typename T>
auto enumerate(const T &range) -> boost::iterator_range<boost::zip_iterator<boost::tuple<
boost::counting_iterator<decltype(boost::distance(range))>, decltype(std::begin(range))>>>
{
return zip(boost::make_iterator_range(boost::make_counting_iterator(0),
boost::make_counting_iterator(boost::distance(range))), range);
}
for (const auto &tup: enumerate(S)) {
std::cout << (tup.get<0>() ? ", " : "") << tup.get<1>();
}
This is using the zip function from Sequence-zip function for c++11?
Related
Are there versions of C++11 and C++17 Range-Based For Loop iterators that can iterate to a certain position in map?
For example, if a map has 10 key-value elements, then how can I iterate through only the first three key-value elements?
The following codes only iterate through the full range of the map.
//C++11
for(auto m: mapData){
cout << m.first << ": " << m.second << endl;
}
//C++17
for(auto [key, val]: mapData){
cout << key << ": " << val << endl;
}
You either need an external counter to make early exit, eg:
int n = 0;
for(auto&& [k, v] : map)
{
if(++n > 10) break;
std::cout << k << ": " << v << std::endl;
}
Or, if you are not afraid of copying the map, you can do:
auto copy = std::map<...>{map.begin(), std::next(map.begin(), 10)};
for(auto&& [k, v] : copy)
{
std::cout << k << ": " << v << std::endl;
}
Finally, if you can use C++20, then you can simply do this:
#include <ranges>
for(auto&& [k, v] : map | std::views::take(10))
{
std::cout << k << ": " << v << std::endl;
}
Range-based for loops are just syntactic-sugar over normal begin() and end() calls. Your request of a partial iteration are complicated by the fact that std::map does not have random-access iterators -- which incurs a cost per iterator incrementing.
To avoid redundant costs, your best option would be to us range-based for loops to a specific counter with a break:
auto count = 0;
for (const auto& [k,v] : map) {
if (++count > n) { break; }
// process 'k', 'v'
}
If you are looking for a "standard" approach, you can use std::for_each_n which lets you choose the length. You just need to ensure that the count doesn't exceed the length of container:
auto length = std::min(map.size(), n);
std::for_each_n(map.begin(), n, [&](const auto& kv) {
// process kv
});
Though the std::for_each_n approach is largely equivalent to the first approach with a counter.
If you expand to support c++20, your options open up a bit since you can construct a std::ranges::subrange:
for (const auto& [k,v] : std::ranges::subrange(map.begin(), std::advance(map.end(), n)) {
// process 'k', 'v'
}
The downside with this approach is that std::map iterators are not random-access -- which means you are paying the cost of iterating the sequence twice. For the most part, this isn't really worth it just to try to leverage range-based for loops.
Edit: See #InnocentBystander's answer with std::views::take(...) as an alternative, which will effectively produce something equivalent to the count+break-based approach.
std::vector v{1,2,3};
for (auto&& t : v | view::sliding(2)) {
auto&& [first, second] = t; // error - t is a range
}
is there a similar view in range-v3 that can return a tuple?
something like sliding<2>
You could zip together range with drop(range, i) for all i in [1,n) (DEMO):
std::vector v{1,2,3};
namespace view = ranges::view;
for (auto [first, second] : view::zip(v, view::drop(v, 1))) {
std::cout << first << ", " << second << '\n';
}
This will quickly get ugly for larger n, and is almost certainly non-optimal, but far simpler than writing your own view adaptor.
Assuming this is not exactly what you had in mind, but you can write something like:
template <typename T, size_t... I>
auto helper(T&& rng, std::index_sequence<I...>) {
return std::make_tuple(rng[I]...);
}
int main() {
std::vector v{1,2,3,4,5};
for (auto&& t : v | ranges::view::sliding(3)) {
auto&& [first, second, third] = helper(t, std::make_index_sequence<3>{});
std::cout << first << ", " << second << ", " << third << std::endl;
}
}
Otherwise, I don't know how to make compile-time-sized ranges.
In a for loop with auto, an iterator iterates over an unordered_map. Like this:
using RuleIndex = std::unordered_map<uint, Symbol*>;
RuleIndex rule_index;
for(const auto & rule_pair : rule_index ) {
std::cout << rule_pair.first << ": ";
printList(rule_pair.second, 0);
std::cout << std::endl;
}
Assume all variables are defined properly, since the code works fine. My question, how can I exclude the first iteration? For example, the map contains 3 rows and current loop iterates for 0, 1, 2. I want to iterate over 1 and 2 only.
bool is_first_iteration = true;
for(const auto & rule_pair : rule_index) {
if (std::exchange(is_first_iteration, false)) continue;
std::cout << rule_pair.first << ": ";
printList(rule_pair.second, 0);
std::cout << std::endl;
}
The std::exchange call assigns false to is_first_iteration and returns the previous value. This is actually one of the use cases discussed in the paper proposing std::exchange for C++14. That paper also shows a reference implementation you can use if you are stuck with C++11.
If you can't use std::exchange (due to C++11 restriction), this simple solution could work as well:
bool is_first_iteration = true;
for (const auto & rule_pair : rule_index)
{
if (is_first_iteration)
{
is_first_iteration = false;
continue;
}
std::cout << rule_pair.first << ": ";
printList(rule_pair.second, 0);
std::cout << std::endl;
}
A terse C++11 option I sometimes use, which keeps a sometimes-handy counter too. I've shown if (i++) below which relies on 0's conversion to false while other numbers convert to true, but you could put if (++i > 1) if you were more comfortable with that:
size_t i = 0;
for (const auto & rule_pair : rule_index)
if (i++)
{
...
}
...or if (++i == 1) continue;... if you prefer...
While easy to write, concise and sometimes helpful, these may be less ammenable to optimisation than a boolean version - benchmark if you care.
Yet another approach that's sometimes useful:
for (const auto & rule_pair : rule_index)
if (&rule_pair != &*std::begin(rule_index))
{
...
}
Pretty new to C++, only at it a week or so, I want to iterate through a set of nested sets and write each element in the inner set to a line in a file.
Each inner set has 3 elements and I want all three elements on the same line.
I have a set up as follows:
// Define "bigSet" and initiate as empty set "Triplets"
typedef set < set<string> > bigSet;
bigSet Triplets;
I tried something of this sort to go through it but it gives me an error...
// Iterate through and print output
set <string>::iterator it;
for(it = Triplets.begin(); it != Triplets.end(); it++){
cout << *it << endl;
}
Any help is greatly appreciated guys thank you!
I would do it this way:
// Iterate through and print output
set < set <string> >::iterator it_ex; // iterator for the "outer" structure
set <string>::iterator it_in; // iterator for the "inner" structure
for(it_ex = Triplets.begin(); it_ex != Triplets.end(); it_ex++)
{
for(it_in = it_ex->begin(); it_in != it_ex->end(); it_in++)
cout << *it_in << ", ";
cout << endl;
}
Triplets is not a set<string>; it is a set<set<string>>; each item in Triplets is itself a set, than can contain several strings.
The iterator must match the type of the container; with two levels of nested containers, you should iterate twice:
set<set<string>>::iterator it;
set<string>::iterator it2;
for(it = Triplets.begin(); it != Triplets.end(); it++) {
for (it2 = it->begin(); it2 != it->end(); ++it2) {
cout << *it2 << endl;
}
}
Triplets is type set < set<string> > and therefore requires an iterator of type set < set<string> >::iterator or bigSet::iterator. It isn't type set <string>. You could also use const_iterator.
Note that iterating Triplets gives you an iterator to another set, and not a string.
Also consider
for (const auto& i : Triplets)
{
for (const auto& j : i)
{
cout << j << endl;
}
}
You have an error because Triplets.begin() is not of type set<string>::iterator, it's set<set<string>>::iterator.
What you need to do is have two loops: one for iterating over the outer set and one for the inner.
set<set<string>>::iterator it;
for(it = Triplets.begin(); it != Triplets.end(); ++it)
{
set<string>::iterator it2;
for(it2 = it->begin(); it2 != it->end(); ++it2)
{
cout << *it2;
}
cout << endl;
}
If you use increment/decrement operators (++/--) on iterators, it might be better to use the prefix versions (++it) instead of the suffix ones (it++). This is because the suffix ones create a copy of the iterator before it is incremented (and that copy is then returned) but in cases like this, you have no need for it.
Moreover, if you're using C++11, you can use the range-based for loops and auto keyword, which simplify things a lot:
for(const auto &innerSet : Triplets)
{
for(const auto &innerSetElement : innerSet)
{
cout << innerSetElement;
}
cout << endl;
}
First: if they're triplets, are you sure that std::set is the type you
want for the inner values. Perhaps a class would be more
appropriate, in which case, you define an operator<< for the `class,
and your simple loop works perfectly. Something like:
class Triplet
{
std::string x;
std::string y;
std::string z;
public:
// Constructors to enforce that none of the entries are identical...
// Accessors, etc.
friend std::ostream& operator<<( std::ostream& dest, Triplet )
{
dest << x << ", " << y << ", " << z;
return dest;
}
};
And then to output:
for ( Triplet const& elem : Triplets ) {
std::cout << elem << std::endl;
}
Otherwise: you need to define the format you want for the output. In
particular, you'll probably want a separator between the strings in the
line, for example. Which means you probably cannot use a range based
for, at least not for the inner loop. You would need something like:
for ( std::set<std::string> const& triplet : Triplets ) {
for ( auto it = triplet.cbegin(); it != triplet.cend(); ++it ) {
if ( it != triplet.cebegin() ) {
std::cout << ", ";
}
std::cout << *it;
}
std::cout << std::endl;
}
(If the set of triplets is large, you'll definitely want to consider
replacing std::endl with '\n'. But of course, if it is really
large, you probably won't be outputting to std::cout.)
Here is my code using STL library, where I try inserting a node at the end, in the middle and in front. For inserting in the middle, I want to provide insertion after a specific node, and not by incrementing the iterator by 2, as I might not know what to increment it by if it is a long list,
Kindly help why is find function not working:
#include <iostream>
#include <list>
#include <string>
using namespace std;
void printlist(list<int> l)
{
list<int>::iterator it = l.begin();
for (it; it != l.end(); ++it)
{
cout << "printlist function call list items: " << *it << endl;
}
}
int main()
{
list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
list<int>::iterator it = l.begin();
cout << 1 << endl;
printlist(l);
l.push_front(0);
cout << 2 << endl;
printlist(l);
it = l.find(l.begin(), l.end(), 2);
l.insert(it, 25);
cout << 3 << endl;
printlist(l);
return 0;
}
Thanks...
std::list<> doesn't have a find() method. You can use the standard algorithm std::find() declared in <algorithm>:
it = std::find(l.begin(), l.end(), 2);
See the answer by #0x499602D2.
But to elaborate on an important point raised in a comment by #NeilKirk, you wrote:
void printlist(list<int> l)
{
list<int>::iterator it = l.begin();
for (it; it != l.end(); ++it)
{
cout << "printlist function call list items: " << *it << endl;
}
}
Note that you are passing the list l by value, not by reference. Passing a class by value (that has not been designed to use implicit sharing) will make a copy. Thus, l will be a copy of the parameter passed. If your list contained a million elements, then passing it by value will make a million-element-copy. You can fix that with:
void printlist(list<int> & l) { ... }
Or if you don't plan on making any changes, it's always nice to announce that with:
void printlist(list<int> const & l) { ... }
Also, C++11 has a range-based for which does the iterator begin/end stuff under the hood for you, and automatic variable typing:
void printlist(list<int> const & l)
{
for (auto i : l)
{
cout << "printlist function call list items: " << i << endl;
}
}
Lots of ways to get fancy in that spirit. But the more critical thing is not go making copies of your data structures, passing them by value when you don't need to!