简体   繁体   中英

how to iterate through a set of sets C++

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!

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;
    }
}

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 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 .)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM