Search code examples
c++generic-programming

Why list::unique removes only consecutive elements in C++?


I had found that list::unique() removes only consecutive elements in a list. I would like to know the reason why the method functions like this.

Remove duplicates from a list explains how to remove the duplicate elements from a list but it doesn't explain why just the consecutive elements are only removed.

The following is my test code:

#include <algorithm>
#include <iostream>
#include <list>
#include <string>
#include <utility>

using namespace std;

void print_message( const pair<int,string>& message )
{
    cout << message.first << " - " << message.second << endl;
}

int main( )
{
list< pair< int, string > > message;
list< pair< int, string > >::iterator ip;

message.push_back( make_pair( 1, string("Test Foo One")) );
message.push_back( make_pair( 1, string("Test Foo Two")) );
message.push_back( make_pair( 1, string("Test Foo Two")) );
message.push_back( make_pair( 1, string("Test Foo Three" )) );
message.push_back( make_pair( 1, string("Test Foo Two" )));
message.push_back( make_pair( 1, string("Test Foo Two")) );

cout << "ORIGINAL MESSAGES" << endl;
ip = message.begin();
while(ip!=message.end()) {
        print_message(*ip);
        ip++;
}

message.unique();
cout << "\nSEQUENTIAL DUPLICATES REMOVED" << endl;
ip = message.begin();
while(ip!=message.end()) {
        print_message(*ip);
        ip++;
}

}

Why is this method list::unique() designed just to remove consecutive duplicate elements in the list?


Solution

  • Design of the standard library follows the approach of providing the user with a comprehensive set of basic, relatively minimalistic algorithmic "building blocks", which, if necessary, can be manually combined into more complex algorithms. Removal of consecutive equivalent elements happens to such minimalistic algorithm. Removal of non-consecutive equivalent elements in a non-ordered sequence is definitely not a minimalistic algorithm. It is not a natural algorithm for a non-ordered sequence.

    Typically, in order to achieve the latter in an non-ordered container the user would have to perform sort and then perform unique. Since the library already provides sort and unique as basic "building blocks", there's no need to provide a separate version of unique algorithm that would work with non-adjacent elements. In that sense sort and unique form an idiomatic pair, just like remove_if and erase in the well-known erase–remove idiom.

    Also, the problem with implementing non-adjacent unique is that a "clean" implementation of such functionality would call for a non-reordering operation, i.e. repetitive elements should be removed, but the relative order of the remaining elements should remain unchanged. It is not possible to implement such functionality as a minimalistic "building block". And I'd say that there's not much need for it, since in most practical cases the user will either be happy with sort-then-unique combination or use a different container type altogether.