50

If I have a vector of values and want to check that they are all the same, what is the best way to do this in C++ efficiently? If I were programming in some other language like R one way my minds jumps to is to return only the unique elements of the container and then if the length of the unique elements is more than 1, I know all the elements cannot be the same. In C++ this can be done like this:

//build an int vector
std::sort(myvector.begin(), myvector.end());
std::vector<int>::iterator it;
//Use unique algorithm to get the unique values.
it = std::unique(myvector.begin(), myvector.end());
positions.resize(std::distance(myvector.begin(),it));
if (myvector.size() > 1) {
    std::cout << "All elements are not the same!" << std::endl;
}

However reading on the internet and SO, I see other answers such using a set or the find_if algorithm. So what is the most efficient way of doing this and why? I imagine mine is not the best way since it involves sorting every element and then a resizing of the vector - but maybe I'm wrong.

cigien
  • 57,834
  • 11
  • 73
  • 112
SJWard
  • 3,629
  • 5
  • 39
  • 54
  • 2
    This has been asked before: http://stackoverflow.com/questions/15531258/test-if-all-elements-of-a-vector-are-equal The answers there point out, importantly, that efficiency in the O(n) compare-all-to-the-first method can be gained by making sure you break off the loop once you find the first element that is not equal. – jogojapan Nov 29 '13 at 13:48
  • Array version: http://stackoverflow.com/questions/14120346/c-fastest-method-to-check-if-all-array-elements-are-equal is a subset via `data()`. – Ciro Santilli OurBigBook.com Apr 28 '16 at 14:29
  • What is the preferred behavior on empty vector? The std::equal and std::adjacent_find answers return false, std::find_if and std::all_of return true. – Andreas Jun 17 '20 at 07:06

16 Answers16

87

You need not to use std::sort. It can be done in a simpler way:

if ( std::adjacent_find( myvector.begin(), myvector.end(), std::not_equal_to<>() ) == myvector.end() )
{
    std::cout << "All elements are equal each other" << std::endl;
}
Blaze
  • 16,736
  • 2
  • 25
  • 44
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    I was close to posting a solution using `adjacent_find`, but mine would have featured a lambda predicate, which is why I did not post it in the end. `not_equal_to` was the missing piece that makes it a beautiful solution. – ComicSansMS Nov 29 '13 at 14:19
  • 5
    It is strange that nobody mentioned std::all_of.:) – Vlad from Moscow Nov 29 '13 at 14:54
  • 3
    Your comment suggests there's a better solution using all_of. If so, could you (or somebody) edit your answer to show it? – Larry Engholm Dec 12 '17 at 00:17
46

you can use std::equal

version 1:

//assuming v has at least 1 element
if ( std::equal(v.begin() + 1, v.end(), v.begin()) )
{
    //all equal
}

This will compare each element with the previous one.

version 2:

//assuming v has at least 1 element
int e = v[0]; //preferably "const auto& e" instead
bool all_equal = true;
for(std::size_t i = 1,s = v.size();i<s && all_equal;i++)
    all_equal = e == v[i];

Edit:

Regarding performance, after testing with 100m elements i found out that in Visual Studio 2015 version 1 is about twice as fast as version 2. This is because the latest compiler for vs2015 uses sse instructions in c++ std implementations when you use ints, float , etc..

if you use _mm_testc_si128 you will get a similar performance to std::equal

Raxvan
  • 6,257
  • 2
  • 25
  • 46
19

using std::all_of and C++11 lambda

if (all_of(values.begin(), values.end(), [&] (int i) {return i == values[0];})){
    //all are the same
}
Anoop K. Prabhu
  • 5,417
  • 2
  • 26
  • 43
Alfonce Nzioka
  • 191
  • 1
  • 3
  • maybe also do begin()+1? – Mikhail Mar 31 '21 at 22:29
  • 1
    @Mikhail, if you can guarantee that `values` is non-empty, `begin()+1` will indeed skip one unnecessary evaluation. But if emptiness _is_ a possibility, the above answer provides safety as it simply returns true in this case. – RyanCu Feb 09 '22 at 01:20
15

Given no constraints on the vector, you have to iterate through the vector at least once, no matter the approach. So just pick the first element and check that all others are equal to it.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
5

While the asymptotic complexity of std::unique is linear, the actual cost of the operation is probably much larger than you need, and it is an inplace algorithm (it will modify the data as it goes).

The fastest approach is to assume that if the vector contains a single element, it is unique by definition. If the vector contains more elements, then you just need to check whether all of them are exactly equal to the first. For that you only need to find the first element that differs from the first, starting the search from the second. If there is such an element, the elements are not unique.

if (v.size() < 2) return true;
auto different = std::find_if(v.begin()+1, v.end(), 
                              [&v](auto const &x) { x != v[0]; });
return different == v.end();

That is using C++14 syntax, in an C++11 toolchain you can use the correct type in the lambda. In C++03 you could use a combination of std::not, std::bind1st/std::bind2nd and std::equal in place of the lambda.

The cost of this approach is distance(start,different element) comparisons and no copies. Expected and worst case linear cost in the number of comparisons (and no copies!)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Opening statement is misleading. Yes Unique is linear, but it followed on a sort, which is definitely not linear. – RichardPlunkett Nov 29 '13 at 14:47
  • @RichardPlunkett: If your only expectation is to detect whether there is a unique value or not, you don't need to sort. Note that this is not attempting to solve the generic problem of removing duplicates or finding how many unique values there are, but rather finding whether there is at least one non-duplicate value. Maybe I should make that more explicit in the answer... although it is just a comment on the question's approach, rather than my own approach. – David Rodríguez - dribeas Nov 29 '13 at 15:02
4

Sorting is an O(NlogN) task.

This is easily solvable in O(N), so your current method is poor.

A simple O(N) would be as Luchian Grigore suggests, iterate over the vector, just once, comparing every element to the first element.

RichardPlunkett
  • 2,998
  • 14
  • 14
3
if(std::all_of(myvector.begin()+1, myvector.end(), std::bind(std::equal_to<int>(),
                                      std::placeholders::_1, myvector.front())) {
  // all members are equal
}
iNFINITEi
  • 1,504
  • 16
  • 23
2

You can use FunctionalPlus(https://github.com/Dobiasd/FunctionalPlus):

std::vector<std::string> things = {"same old", "same old"};
if (fplus::all_the_same(things))
    std::cout << "All things being equal." << std::endl;
Tas
  • 7,023
  • 3
  • 36
  • 51
uol3c
  • 559
  • 5
  • 9
2

Maybe something like this. It traverses vector just once and does not mess with the vector content.

std::vector<int> values { 5, 5, 5, 4 };
bool equal = std::count_if(values.begin(), values.end(), [ &values ] (auto size) { return size == values[0]; }) == values.size();

If the values in the vector are something different than basic type you have to implement equality operator.

After taking into account underscore_d remarks, I'm changing possible solution

std::vector<int> values { 5, 5, 5, 4 };
bool equal = std::all_of(values.begin(),values.end(),[ &values ] (auto item) { return item == values[0]; });
Rostfrei
  • 454
  • 6
  • 12
  • It's also a waste of time because `count` has to keep going even after it finds the 1st differing element, but you only need to know if there was one, so those are wasted cycles. I don't see why we needed an inefficient alternative 4 years later. – underscore_d Sep 26 '18 at 16:33
1

In your specific case, iterating over vector element and finding a different element from the first one would be enough. You may even be lucky enough to stop before evaluating all the elements in your vector. (A while loop could be used but I sticked with a for loop for readability reasons)

bool uniqueElt = true;
int firstItem = *myvector.begin();
for (std::vector<int>::const_iterator it = myvector.begin()+1; it != myvector.end() ; ++it) {
    if(*it != firstItem) {
        uniqueElt = false;
        break;
    }
}

In case you want to know how many different values your vector contains, you could build a set and check its size to see how many different values are inside:

std::set mySet;
std::copy(mySet.begin(), myvector.begin(), myvector.end());
Julien B.
  • 88
  • 6
1

You can simply use std::count to count all the elements that match the starting element:

std::vector<int> numbers = { 5, 5, 5, 5, 5, 5, 5 };
if (std::count(std::begin(numbers), std::end(numbers), numbers.front()) == numbers.size())
{
    std::cout << "Elements are all the same" << std::endl;
}
Tas
  • 7,023
  • 3
  • 36
  • 51
  • 4
    I don't see how this is any 'simpler' than all the other ways posted 4 years earlier. In fact, it's a waste of time because `count` has to keep going even after it finds the 1st differing element, but you only need to know if there was one, so those are purely wasted cycles. – underscore_d Sep 26 '18 at 16:34
  • You raise a good point, at the time I hadn't considered that it would iterate over all elements; however, many of the other solutions do the same, and I don't claim this is the best method of doing it, but I ran into this issue and found this to be the most readable solution. If a `vector` is very large then it would definitely be worth using a solution that wouldn't iterate over all elements. – Tas Sep 26 '18 at 22:07
1

LLVM provides some independently usable headers+libraries:

#include <llvm/ADT/STLExtras.h>
if (llvm::is_splat(myvector))
  std::cout << "All elements are the same!" << std::endl;

https://godbolt.org/z/fQX-jc

Jan Kratochvil
  • 387
  • 3
  • 11
0

for the sake of completeness, because it still isn't the most efficient, you can use std::unique in a more efficient way to decide whether all members are the same, but beware that after using std::unique this way the container is useless:

#include <algorithm>
#include <iterator>

if (std::distance(cntnr.begin(), std::unique(cntnr.begin(), cntnr.end()) == 1)
{
  // all members were the same, but
}
stefaanv
  • 14,072
  • 2
  • 31
  • 53
0

Another approach using C++ 14:

bool allEqual = accumulate(v.begin(), v.end(), true, [first = v[0]](bool acc, int b) {
    return acc && (b == first);
  });

which is also order N.

Batato
  • 560
  • 5
  • 18
0

Here is a readable C++17 solution which might remind students of the other constructors of std::vector:

if (v==std::vector(v.size(),v[0])) {
  // you guys are all the same
}

...before C++17, the std::vector rvalue would need its type provided explicitly:

if (v==std::vector<typename decltype(v)::value_type>(v.size(),v[0])) {
  // you guys are all the same
}
user2023370
  • 10,488
  • 6
  • 50
  • 83
0

The C++ function is defined in library in STL. This function operates on whole range of array elements and can save time to run a loop to check each elements one by one. It checks for a given property on every element and returns true when each element in range satisfies specified property, else returns false.

// C++ code to demonstrate working of all_of()
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
    std::vector<int> v(10, 2);
    
    // illustrate all_of
    if (std::all_of(v.cbegin(), v.cend(), [](int i){ return i % 2 == 0; }))
    {
        std::cout << "All numbers are even\n";
    }

}