1

std::set<std::string> &uniq_email_list;

I have a set of many email elements, for examples:

('foo@bar.com', 'foo2@bar.com', 'foo@bazz.com', 'foo2@bazz.com','zfoo@bar.com')

then I write these elements to a file

for(iter = uniq_email_list.begin() ; iter!=uniq_email_list.end(); ++iter){  
            output_file<< *iter << std::endl;
        }

Before I write these elements to the file, I want to sort by '@' next domain name and I want it to look like this in the file

foo@bar.com
foo2@bar.com
zfoo@bar.com
foo@bazz.com
foo2@bazz.com

And I know

You cannot resort a set, how it sorts is part of the type of the particular set. A given set has a fixed set order that cannot be changed.

You could create a new set with the same data relatively easily. Just create a new set that sorts based on the new criteria

I read that post Sorting Sets using std::sort But I couldn't find the answer to my problem.

As a result of my research I found something like

std::set<string>::iterator &it;
it=myset.find('@');

Can I sort by the returned address with this structure? Or if there are other solutions suitable for this problem, thank you in advance, I am ready to get advice on issues related to C ++ solutions.

testerenecmi
  • 34
  • 1
  • 8
  • Please do not use "C/C++". C and C++ are very different languages. What you show is clearly no C. – Gerhardh Jan 29 '21 at 12:06
  • What does "this structure" mean? No, a `std::set` cannot be sorted, no matter what other structures exist in the code. – Sam Varshavchik Jan 29 '21 at 12:13
  • You can set custom comparator to `std::set`. [std::set::set - cppreference.com](https://en.cppreference.com/w/cpp/container/set/set) – MikeCAT Jan 29 '21 at 12:17

2 Answers2

4

If you are going to use the domain and name parts individually, I suggest that you split them up and put them in an email class for which you provide an operator< that fulfills the Compare requirement needed to create a std::set<email>:

#include <tuple>  // std::tie

struct email {
    std::string name;
    std::string domain;

    // The compare operator used by default by a std::set<email>:
    bool operator<(const email& rhs) const {

        // using std::tie makes creating an operator< that does
        // strict weak ordering relatively easy:

        return std::tie(domain, name) < std::tie(rhs.domain, rhs.name);
    }
};

std::ostream& operator<<(std::ostream& os, const email& e) {
    os << e.name;
    if(not e.domain.empty()) os << '@' << e.domain;
    return os;
}

std::istream& operator>>(std::istream& is, email& e) {
    if(std::string tmp; is >> tmp) {

        auto at_pos = tmp.find('@');        
        e.name = tmp.substr(0, at_pos);

        if(at_pos != std::string::npos) {    
            e.domain = tmp.substr(at_pos + 1);
        } else {
            e.domain.clear();
        }
    }
    return is;
}

You can now put them in a std::set<email> or in a std::vector<email> (and std::sort that vector) and stream them out in the order specified by the member operator<. Domain first, name last and also read from streams:

email tmp;
while(instream >> tmp) {
    std::cout << "read address: " << tmp << '\n';
}

Demo

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
2
class EmailCompare
{
public:
    bool operator()(const std::string& a, const std::string& b) const {
        // your code here
        return ... ;
    }
};

using EmailSet = std::set<std::string, EmailCompare>;
acraig5075
  • 10,588
  • 3
  • 31
  • 50
Marek R
  • 32,568
  • 6
  • 55
  • 140
  • fwiw, I answered along the same line, but only now realized that using a custom comparator that only compares the part after `@`, one cannot have `foo2@bar.com` and `zfoo@bar.com` in the set at the same time. Maybe there is an obvious solution that I don't see... – 463035818_is_not_an_ai Jan 29 '21 at 13:32
  • `// your code here` should compare not only host but user too. – Marek R Jan 29 '21 at 14:09
  • yeah makes sense, instead of comparing `foo2@bar.com` vs `zfoo@bar.com` compare `@bar.comfoo2` vs `@bar.comzfoo`. – 463035818_is_not_an_ai Jan 29 '21 at 14:12