How can I get the current position without another variable? - c++

Many times when creating a grammatical list (with comas), I use similar code to the following:
std::stringstream list;
int i = 0;
for (auto itemListIt = itemList.begin(); itemListIt != itemList.end(); itemListIt++)
{
list << *itemListIt;
if (i < itemList.size() - 1) list << ", ";
i++;
}
Is there some more concise way do this, perhaps without the extra variable - 'i'?

Why not test what you're really interested in; "Is there another element after this one?".
std::stringstream list;
for (auto it = roomList.begin(); it != itemList.end(); it++)
{
list << *it;
if ( it+1 != itemList.end() ) list << ", ";
}

There are two simple solutions for this. The first is to use a while
loop:
auto itemListIt = itemList.begin();
while ( itemListIt != itemList.end() ) {
list << *itemListIt;
++ itemListIt;
if ( itemListIt != itemList.end() ) {
list << ", ";
}
}
The second solution is to change the logic slightly: instead of
appending a ", " if there is more to follow, prefix one if you're not
the first element:
for ( auto itemListIt = itemList.begin(); itemListIt != itemList.end(); ++ itemListIt ) {
if ( itemListIt != itemList.begin() ) {
list << ", ";
}
list << *itemListIt;
}

You can loop over everything up to the next to last using --items.end().
Then output the last one using items.back().
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>
#include <iostream>
int main()
{
std::ostringstream oss;
std::vector<int> items;
items.push_back(1);
items.push_back(1);
items.push_back(2);
items.push_back(3);
items.push_back(5);
items.push_back(8);
if(items.size() > 1)
{
std::copy(items.begin(), --items.end(),
std::ostream_iterator<int>(oss, ", "));
oss << "and ";
}
// else do nothing
oss << items.back();
std::cout << oss.str();
}
Output:
1, 1, 2, 3, 5, and 8

The following will work with any InputIterator input:
std::stringstream list;
auto it(std::begin(input)); //Or however you get the input
auto end(std::end(input));
bool first(true);
for (; it != end; ++it)
{
if (!first) list << ", ";
else first = false;
list << *it;
}
Or without an extra variable:
std::stringstream list;
auto it(std::begin(input)); //Or however you get the input
auto end(std::end(input));
if (it != end)
{
list << *it;
++it;
}
for (; it != end; ++it)
{
list << ", " << *it;
}

And if you want to do that with a map or other iterators that cannot do random access, as others have suggested, check for the first element:
std::stringstream query;
query << "select id from dueShipments where package in (";
for (auto it = packageList.begin(); it != packageList.end(); it++)
{
if (it != packageList.begin()) query << ", ";
query << it->second;
}
query << ")";

Related

How to use the member type iterator of std::list with a while loop to make simple changes to a list

I create and modify a simple list. I replace the element at index 1 of the list. How would I semantically accomplish the same thing with a while loop. The tutorial instructor remarked that the current code is quite ugly and a while loop would accomplish the same thing in a much more simple and pretty fashion. I can't figure it out.
#include <iostream>
#include <list>
int main() {
std::list<int> numbers;
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_front(0);
std::list<int>::iterator it = numbers.begin();
it++;
numbers.insert(it, 100);
std::cout << "Current element is: " << *it << '\n';
std::list<int>::iterator eraseIt = numbers.begin();
eraseIt++;
eraseIt = numbers.erase(eraseIt);
std::cout << "erasing at element: " << *eraseIt << '\n';
for (std::list<int>::iterator it = numbers.begin(); it != numbers.end();) {
if (*it == 2) {
numbers.insert(it, 1234);
}
if (*it == 1) {
it = numbers.erase(it);
} else {
it++;
}
}
for (std::list<int>::iterator it = numbers.begin(); it != numbers.end();
it++) {
std::cout << *it << '\n';
}
return 0;
}
You can probably do this if iterators are required:
// ...
std::list<int>::iterator it = ++numbers.begin();
numbers.insert(it, 100);
std::cout << "Current element is: " << *it << '\n';
std::list<int>::iterator eraseIt = ++numbers.begin();
eraseIt = numbers.erase(eraseIt);
std::cout << "erasing at element: " << *eraseIt << '\n';
it = numbers.begin();
while (it != numbers.end())
{
if (*it == 2) {
numbers.insert(it, 1234);
}
if (*it == 1) {
it = numbers.erase(it);
}
else {
++it;
}
}
for (auto& i : numbers)
{
std::cout << i << std::endl;
}
// ...

How to peek next and prev elements in a boost set<cpp_int>

I am trying to store boost integers cpp_int in an ordered set and check for next and prev elements using below code:
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/unordered_set.hpp>
#include <iostream>
namespace mp = boost::multiprecision;
using boost::unordered_set;
using namespace std;
int main() {
set<mp::cpp_int> st;
set<mp::cpp_int>::iterator it, it1, it2;
//pair<set<mp::cpp_int>::iterator,bool> res;
boost::tuples::tuple<set<mp::cpp_int>::iterator, bool> tp;
int i = 0, temp;
while(i<10){
cin>>temp;
tp = st.insert(temp);
it = get<0>(tp);
it1 = prev(it);
it2 = next(it);
cout<<*it1<<endl;
//cout<<*it2<<endl;
i++;
}
return 0;
}
However, the above code does not work as expected and crashes after couple of inputs. One such crashing sequence of inputs is:
0
1
2
3
4
0
What is the proper way of using set and iterators when using boost?
You need to check that there is a previous / next element before dereferencing the it1 and it2, e.g.:
std::set<mp::cpp_int> s;
for (size_t i = 0; i < 10; ++i){
std::cin >> temp;
auto p = s.insert(temp);
if (p.second) { // insertion succeed
auto it = p.first;
std::cout << "Inserted: " << *it << '\n';
if (it != s.begin()) { // not the first, there is a previous element
auto it1 = std::prev(it);
std::cout << "Previous: " << *it1 << '\n';
}
else {
std::cout << "Previous: None\n";
}
auto it2 = std::next(it);
if (it2 != s.end()) { // there is a next element
std::cout << "Next: " << *it2 << '\n';
}
else {
std::cout << "Next: None\n";
}
}
}
Also, if you want to find the previous and next elements of an existing element, you should use std::set::find, not std::set::insert:
auto it = s.find(temp);
if (it != s.end()) {
// Same code as above.
}

C++ printing a list of sets

I'm trying to print out a list of sets but I'm confused with the syntax. I want each set to be on a new line. Here is my code:
set<int> set1 = { 2, 4, 5 };
set<int> set2 = { 4, 5 };
list<set<int>> list1;
list<set<int>>::iterator it = list1.begin();
list1.insert(it, set1);
list1.insert(it, set2);
cout << "List contents:" << endl;
for (it = list1.begin(); it != list1.end(); ++it)
{
cout << *it; //error is here
}
I'm getting an error when trying to print the pointer to the iterator. Pretty sure its because I'm using a set inside of the list, but I don't know the proper syntax for outputting this list.
Do you want to print as following?
for (it = list1.begin(); it != list1.end(); ++it)
{
for (set<int>::iterator s = it->begin(); s != it->end(); s++) {
cout << *s << ' ';
}
cout << endl;
}
output:
List contents:
2 4 5
4 5
There is no overload of operator << for std::set, you have to write the loop yourself (and possibly creating a function for that)
With for range, you may simply do:
for (const auto& s : list1) {
for (int i : s) {
std::cout << i << ' ';
}
std::cout << std::endl;
}

C++ Iterator access next element for comparison

I'm trying to compare two elements in a List by 'peeking' into the next element in the list. Using C++11.
Is this possible? I'm having some trouble.
#include <list>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
list<int> intList;
intList.push_back(10);
intList.push_back(20);
intList.push_back(30);
intList.push_back(30);
list<int>::iterator it;
for (it = intList.begin(); it != intList.end(); it++)
{
if (*it == *it + 1)
cout << "Duplicate: " << *it << '\n';
}
}
Yes, it's possible:
assert(!intList.empty()); // else ++begin is UB
for (list<int>::iterator it1 = intList.begin(), it2 = ++intList.begin();
it2 != intList.end(); ++it1, ++it2)
{
if (*it1 == *it2)
cout << "Duplicate: " << *it1 << '\n';
}
Your search can be simplified by using std::adjacent_find() instead (which supports std::list iterators):
Searches the range [first, last) for two consecutive identical elements.
For example:
list<int>::iterator it = std::adjacent_find(intList.begin(), intList.end());
if (it != intList.end())
cout << "Duplicate: " << *it << '\n';`
It's possible but your confronting *it with *it augmented by 1 (that is even different).
I suppose your intention was to confront two adiacent element of the list, so [thanks to DeiDei for the correction]
if ( false == intList.empty() )
{
auto it { intList.cbegin() };
auto oldVal { *it };
for ( ; ++it != intList.cend() ; oldVal = *it )
{
if ( *it == oldVal )
cout << "Duplicate: " << oldVal << '\n';
}
}
p.s.: sorry for my bad English

Checking if current element is last element of set

I am trying to write a print function for set in C++, and this is what I wrote :
void print_set(set<int> &s)
{
cout<<"{";
for(auto it = s.begin() ; it!=s.end() ; ++it)
{
cout<<*it;
if(it!=(s.end()-1)) //shows error here
cout<<",";
}
cout<<"}";
}
But I am getting error. How can I check whether current element is last element or not ?
set's integers are not random-access, so you can't do arithmetic on them. Use std::prev instead.
May I suggest an alternative approach?
Print the comma before each element, except the first:
void print_set(set<int> &s)
{
cout << "{";
for(auto it = s.begin() ; it != s.end() ; ++it)
{
if(it != s.begin())
cout << ", ";
cout << *it;
}
cout << "}";
}
Simply check whether the next element equals the end:
auto next = it;
++next;
if (next != s.end())
cout << ",";
You can only apply the ++ and -- operators on set iterators. Adding a number is not defined. You can make your code work like so:
void print_set(set<int> &s)
{
cout<<"{";
auto second_to_last = s.end();
if (!s.empty()) {
second_to_last--;
}
for(auto it = s.begin() ; it!=s.end() ; ++it)
{
cout<<*it;
if(it!=second_to_last) {
cout<<", ";
}
}
cout<<"}";
}
What this code does is essentially store an iterator to the second to last element once and then compare the element you have with it. Please note that second_to_last will not be accurate if the set is empty but the code will still work as expected.
What I do in this case:
void print_set(const std::set<int>& s)
{
const char* sep = "";
std::cout << "{";
for (int e : s)
{
std::cout << sep << e;
sep = ", ";
}
std::cout << "}";
}