30

I have the following for each C++ code:

for (auto item : myVector)
{
    std::cout << item;

    if (item == orderBy.IsLast())       // <--- Check if this is the last element
        std::cout << "(Is last element) " << std::endl;
    else if (item == orderBy.IsFirst()) // <-- Check if this is the first element
        std::cout << "(Is first element)" << std::endl;
}

Of course IfLast() and IfFirst() do not exist on std::vector. Is there a native std:: way to check for first and last element ?

phuclv
  • 37,963
  • 15
  • 156
  • 475
Mendes
  • 17,489
  • 35
  • 150
  • 263
  • 2
    If it is only special operations on head/tail elements, consider bring it out of loop. – squid May 13 '15 at 03:38
  • 2
    Prefer `for(auto& item: myVector)` or `for(const auto& item: myVector)` to prevent copy and mutable. – Chen OT May 13 '15 at 03:46
  • Another pre-C++11 version: https://stackoverflow.com/questions/151046/how-can-i-detect-the-last-iteration-in-a-loop-over-stdmap – pattivacek Sep 22 '17 at 10:24

7 Answers7

34

You shouldn't use the range-based for in this case, as this kind of for "hides" the iterator, and you'd need an additional counter to keep track of the position in vector. You can simply do

for(auto it = myVector.begin(); it != myVector.end(); ++it)
{
    if(it == myVector.begin()) // first element
    {
        // do something
    }
    else if(std::next(it) == myVector.end()) // last element
    {
        // do something else
    }
}

Note that simply comparing my.Vector.back() with your element from a range-based for is OK only if you're sure that you don't have duplicates in the vector. But if e.g. the value of the last element appears multiple times in the vector, you're going to find only its first position. So that's why there's really no good way of using a range-based for without an additional index that keeps track of where exactly in the vector you are.

EDIT See also @thelink2012's answer for how to "trick" your range-based for so you can get the position of the element implicitly.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 1
    `,1` is redundant; or even `it + 1` is fine. – T.C. May 13 '15 at 03:35
  • Can´t understand why I should not use it. Is this a bad practice ? Indeed I don´t need the iterator, but the `item` element... – Mendes May 13 '15 at 03:38
  • 1
    You may also wish to consider a vector whose first element is also its last element :-) – johnsyweb May 13 '15 at 03:39
  • 1
    @Mendez it's not a bad practice. Depends on the scenario. If you want to track the position in the vector, it's better to loop using iterators. If you just want to e.g. display the elements (and not their position), then a range-based for is OK. – vsoftco May 13 '15 at 03:39
29

Use the std::vector::front and std::vector::back to get a reference to the data in the first and last positions.

Reference is a keyword here because you could efficiently check the address of your iterating item and the address of the respective front/back references. In your example you take the item by value not reference so this prehaps wouldn't work, take in consideration this example that'd work with this method:

for(auto& item : myVector) // take item by reference
{
    std::cout << item;
    if (&item == &myVector.back())
       std::cout << "(last element) " << std::endl;
    else if (&item == &myVector.front())
       std::cout << "(first element)" << std::endl;
}

If the object overloads the address of operator & (though it's considered a bad practice) you might want to use std::addressof instead.

This method won't work however for the std::vector<bool> specialization since it optimizes the vector to store booleans efficiently with bits, and since we cannot have references to bits, all references taken out this data structure is a proxy object not exactly tied to the address of the internal data.

Denilson Amorim
  • 9,642
  • 6
  • 27
  • 31
  • 1
    I almost said you're wrong. But taking by reference works here, +1. – vsoftco May 13 '15 at 03:49
  • 4
    It will break on `std::vector` :P (the latter return proxies in a range-based for, which are rvalues, and it's not really a container) But still, nice answer. – vsoftco May 13 '15 at 04:00
  • @vsoftco Very good point that might easily get unoticed! I've updated the answer. Much thanks for the heads up :) – Denilson Amorim May 13 '15 at 04:08
  • @vsoftco: let's hope nobody uses this aberration. – Matthieu M. May 13 '15 at 10:49
  • @MatthieuM. you mean `vector` or the answer? The answer is fine IMO, there's nothing wrong that can happen (unless of course your vector elements overload `operator&`). And even `vector` [has its usage](https://isocpp.org/blog/2012/11/on-vectorbool). – vsoftco May 13 '15 at 14:58
  • @vsoftco: `vector`; there are issues with using proxies, and some expected usage can fail unexpectedly. Regarding the address, `std::address_of` avoids the issue, though it's more verbose. – Matthieu M. May 13 '15 at 15:01
  • @MatthieuM. Yes then I agree. However Howard makes a good point about proper usage of std::vector, I read it and it changed my mind a bit. – vsoftco May 13 '15 at 15:02
  • @vsoftco: Oh I agree, some people proposed the name `dynamic_bitset` for example; but not backward compatible so unlikely to come... – Matthieu M. May 13 '15 at 15:09
  • @MatthieuM. Yes I agree, implementing it as a specialization of `vector` is a bad idea. – vsoftco May 13 '15 at 15:12
5

Use std::vector::front() for the first element.
Use std::vector::back() for the last element.

Before you call those functions, make sure that the vector is not empty.

    if (!orderBy.empty() && item == orderBy.back()) <--- Check if this is the last element

    else if (!orderBy.empty() && item == orderBy.front()) <-- Check if this is the first element
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • what if the vectors has the last element duplicated? It's not gonna work, you have to keep track of the index. – vsoftco May 13 '15 at 03:40
  • @vsoftco, I don't know what the OP's intention is for those checks. Given the posted code they either (1) don't expect to see duplicates, or (2) want to see the output multiple times. – R Sahu May 13 '15 at 03:42
3

For value based comparison, you may use myVector.front()/myVector[0] as the first and myVector.back()/myVector[myVector.size()-1] as the last element.

Suggestion
Capture the reference by default to avoid unwanted copies. e.g.

for(const auto& I : myVector)

iammilind
  • 68,093
  • 33
  • 169
  • 336
2

You could search for it again, but that would be rather inefficient. If you need information on the position of the current item, you probably want to use an iterator or an index:

for (std::size_t i=0; i<myVector.size(); ++i)
{
    auto& item = myVector[i];
    std::cout << item;

    if (i == (myVector.size() - 1))
       std::cout << "(Is last element) " << std::endl;
    else if (i == 0)
       std::cout << "(Is first element)" << std::endl;
}
isanae
  • 3,253
  • 1
  • 22
  • 47
2

If you have special cases for the boundaries you should use the ol' iterator version, but separating the first and last cases away from the loop.

If the cases share the code after that if you should encapsulate it on a function.

I can't write code from my phone :c

Dietr1ch
  • 178
  • 8
0

This works for me

vector<int> vi {1,2,3,4};

cout << "vi = {";
for (const auto &e : vi) {
  cout << e;

  if (&e != &vi.back())
    cout << ',';
}
cout << '}' << endl;
jafox
  • 1