C++AlgorithmsIntermediate

std::unique()

Removes consecutive duplicate elements from a sorted range `[first, last)` and returns an iterator to the new logical end of the range.

Review the syntaxStudy the examplesOpen the coding app
std::unique(first, last, [binary_pred]);

This static page keeps the syntax and examples indexed for search, while the coding app handles interactive exploration and saved references.

What it does

Overview

Removes consecutive duplicate elements from a sorted range `[first, last)` and returns an iterator to the new logical end of the range.

The `std::unique()` algorithm, found in the `<algorithm>` header, is used to eliminate consecutive duplicate elements from a sorted range. It's crucial to understand that `std::unique()` does not actually remove elements from the container; instead, it rearranges the elements such that all unique elements are moved to the beginning of the range, and the relative order of the unique elements is preserved. It returns an iterator to the element that is one past the last unique element. The elements between this returned iterator and the original `last` iterator are left in a valid but unspecified state. To truly remove the duplicate elements and shrink the container, `std::unique()` is typically used in conjunction with a container's `erase()` method (the erase-remove idiom). For example, `vec.erase(std::unique(vec.begin(), vec.end()), vec.end())`. The algorithm has a linear time complexity, O(N), as it makes a single pass through the range. It requires forward iterators and uses `operator==` for comparison by default, but a custom binary predicate can be provided. It's essential that the range is sorted for `std::unique()` to work correctly, as it only removes *consecutive* duplicates.

Quick reference

Syntax

std::unique(first, last, [binary_pred]);

Inputs

Parameters

firstForwardIterator · An iterator to the beginning of the range.
lastForwardIterator · An iterator to the end of the range (one past the last element).
binary_pred (optional)BinaryPredicate · An optional binary predicate that returns true if the two arguments are considered equal.

See it in practice

Examples

1

Removing consecutive duplicates from a sorted vector

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> numbers = {1, 1, 2, 2, 2, 3, 4, 4, 5};

    std::cout << "Original vector: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;

    // unique rearranges elements and returns iterator to new logical end
    auto last_unique = std::unique(numbers.begin(), numbers.end());

    std::cout << "Vector after std::unique: ";
    for (auto it = numbers.begin(); it != last_unique; ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // To actually remove elements and resize the vector
    numbers.erase(last_unique, numbers.end());

    std::cout << "Vector after erase: ";
    for (int n : numbers) std::cout << n << " ";
    std::cout << std::endl;
    return 0;
}
Output:
Original vector: 1 1 2 2 2 3 4 4 5 Vector after std::unique: 1 2 3 4 5 Vector after erase: 1 2 3 4 5

`std::unique()` moves unique elements to the front. The `erase()` method then removes the 'duplicate' elements from the end, resizing the vector.

2

Using std::unique() with a custom predicate

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

struct CaseInsensitiveEqual {
    bool operator()(const std::string& a, const std::string& b) const {
        return std::tolower(a[0]) == std::tolower(b[0]); // Compare first char case-insensitively
    }
};

int main() {
    std::vector<std::string> words = {"Apple", "apple", "Banana", "banana", "Cherry", "Date"};
    // First, sort with a compatible comparison
    std::sort(words.begin(), words.end(), [](const std::string& a, const std::string& b) {
        return std::tolower(a[0]) < std::tolower(b[0]);
    });

    std::cout << "Sorted words: ";
    for (const std::string& w : words) std::cout << w << " ";
    std::cout << std::endl;

    auto last_unique = std::unique(words.begin(), words.end(), CaseInsensitiveEqual());
    words.erase(last_unique, words.end());

    std::cout << "Unique words (case-insensitive first char): ";
    for (const std::string& w : words) std::cout << w << " ";
    std::cout << std::endl;
    return 0;
}
Output:
Sorted words: Apple apple Banana banana Cherry Date Unique words (case-insensitive first char): Apple Banana Cherry Date

A custom predicate `CaseInsensitiveEqual` is used to define equality based on the first character, ignoring case. The range must be sorted according to this custom equality first.

3

Unique on a raw array (without actual removal)

#include <iostream>
#include <algorithm>

int main() {
    int arr[] = {1, 1, 2, 3, 3, 3, 4, 5, 5};
    int n = sizeof(arr) / sizeof(arr[0]);

    std::cout << "Original array: ";
    for (int i = 0; i < n; ++i) std::cout << arr[i] << " ";
    std::cout << std::endl;

    int* new_end = std::unique(arr, arr + n);

    std::cout << "Array after std::unique: ";
    for (int* p = arr; p != new_end; ++p) {
        std::cout << *p << " ";
    }
    std::cout << std::endl;
    // For raw arrays, you'd typically manually manage the size or copy to a new array.
    return 0;
}
Output:
Original array: 1 1 2 3 3 3 4 5 5 Array after std::unique: 1 2 3 4 5

`std::unique()` works on raw arrays. The elements beyond `nd` are left in an unspecified state, and the array's physical size remains unchanged.

Debug faster

Common Errors

1

Logical Error

Cause: Using `std::unique()` on an unsorted range, which will only remove *consecutive* duplicates, not all duplicates.

Fix: Always sort the range using `std::sort()` before calling `std::unique()` if you intend to remove all duplicates.

std::vector<int> v = {1, 3, 2, 1, 4};
auto it = std::unique(v.begin(), v.end()); // v becomes {1, 3, 2, 1, 4} (no change as no consecutive duplicates)
2

Logical Error

Cause: Forgetting to call `erase()` after `std::unique()` when working with containers like `std::vector` or `std::deque`, leading to a container that still holds 'duplicate' elements at the end.

Fix: Always follow `std::unique(first, last)` with `container.erase(result_iterator, last)` to physically remove the elements and resize the container.

std::vector<int> v = {1, 1, 2};
std::unique(v.begin(), v.end());
// v is now {1, 2, 2}, size is still 3. The last '2' is a 'garbage' element.

Runtime support

Compatibility

GCC, Clang, MSVCN/AC++98+

Source: cppreference.com

Common questions

Frequently Asked Questions

Removes consecutive duplicate elements from a sorted range `[first, last)` and returns an iterator to the new logical end of the range.

first: An iterator to the beginning of the range. last: An iterator to the end of the range (one past the last element). red: An optional binary predicate that returns true if the two arguments are considered equal.

Logical Error: Always sort the range using `std::sort()` before calling `std::unique()` if you intend to remove all duplicates. Logical Error: Always follow `std::unique(first, last)` with `container.erase(terator, last)` to physically remove the elements and resize the container.