So if I have a vector of words like:
Vec1 = "words", "words", "are", "fun", "fun"
resulting list: "fun", "words"
I am trying to determine which words are duplicated, and return an alphabetized vector of 1 copy of them. My problem is that I don't even know where to start, the only thing close to it I found was std::unique_copy
which doesn't exactly do what I need. And specifically, I am inputting a std::vector<std::string>
but outputting a std::list<std::string>
. And if needed, I can use functor.
Could someone at least push me in the right direction please? I already tried reading stl documentation,but I am just "brain" blocked right now.
In 3 lines (not counting the vector and list creation nor the superfluous line-breaks in name of readability):
EDIT
Explanation of the solution:
Sorting the vector is needed in order to use
set_difference()
later.The
uvec
set will automatically keep elements sorted, and eliminate duplicates.The
output
list will be populated by the elements ofvec - uvec
.You can get a pretty clean implementation using a std::map to count the occurrences, and then relying on std::list::sort to sort the resulting list of words. For example:
Using a std::map there seems a little wasteful, but it gets the job done.
std::unordered_set<std::string>
Since you want each duplicate only listed once in the results, you can use a hashset (not list) for the results as well.
IMO, Ben Voigt started with a good basic idea, but I would caution against taking his wording too literally.
In particular, I dislike the idea of searching for the string in the set, then adding it to your set if it's not present, and adding it to the output if it was present. This basically means every time we encounter a new word, we search our set of existing words twice, once to check whether a word is present, and again to insert it because it wasn't. Most of that searching will be essentially identical -- unless some other thread mutates the structure in the interim (which could give a race condition).
Instead, I'd start by trying to add it to the set of words you've seen. That returns a
pair<iterator, bool>
, with thebool
set totrue
if and only if the value was inserted -- i.e., was not previously present. That lets us consolidate the search for an existing string and the insertion of the new string together into a single insert:This also cleans up the flow enough that it's pretty easy to turn the test into a functor that we can then use with
std::remove_copy_if
to produce our results quite directly:Depending on whether I cared more about code simplicity or execution speed, I might use an
std::vector
instead of theset
for result, and usestd::sort
followed bystd::unique_copy
to produce the final result. In such a case I'd probably also replace thestd::set
inside ofshow_copies
with anstd::unordered_set
instead:This is marginally more complex (one whole line longer!) but likely to be substantially faster when/if the number of words gets very large. Also note that I'm using
std::unique_copy
primarily to produce visible output. If you just want the result in a collection, you can use the standard unique/erase idiom to get unique items inintermediate
.Here's a better algorithm than the ones other people have proposed:
It's better because it only requires
swap
with no auxiliaryvector
for storage, which means it will behave optimally for earlier versions of C++, and it doesn't require elements to be copyable.If you're more clever, I think you can avoid sorting the vector twice as well.
In place (no additional storage). No string copying (except to result list). One sort + one pass: