79

Everyone encounters this issue at some point:

for(const auto& item : items) {
    cout << item << separator;
}

... and you get an extra separator you don't want at the end. Sometime it's not printing, but, say, performing some other action, but such that consecutive actions of the same type require some separator action - but the last doesn't.

Now, if you work with old-school for loops and an array, you would do

for(int i = 0; i < num_items; i++)
    cout << items[i];
    if (i < num_items - 1) { cout << separator; }
}

(or you could special-case the last item out of the loop.) If you have anything that admits non-destructive iterators, even if you don't know its size, you can do:

for(auto it = items.cbegin(); it != items.cend(); it++) {
    cout << *it;
    if (std::next(it) != items.cend()) { cout << separator; }
}

I dislike the aesthetics of the last two, and like ranged for loops. Can I obtain the same effect as with the last two but using more spiffy C++11ish constructs?


To expand the question further (beyond, say, this one), I'll say I would also like not to expressly have special-case the first or the last element. That's an "implementation detail" which I don't want to be bothered with. So, in imaginary-future-C++, maybe something like:
for(const auto& item : items) {
    cout << item;
} and_between {
    cout << separator;
}
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 5
    If you wanted to take a page from the functional programming book, you can always use [the accumulate method](http://en.cppreference.com/w/cpp/algorithm/accumulate), although this tends to be overkill a lot of the time – Kevin W. Feb 12 '16 at 21:57
  • 4
    C++ does not have algorithmic `join`. This is a huge shame. – SergeyA Feb 12 '16 at 21:57
  • 1
    @KevinW., `fold` is not going to help you - it will apply operation for every element, including the first and the last. – SergeyA Feb 12 '16 at 21:58
  • or http://stackoverflow.com/questions/9729467/last-element-in-foreach – djechlin Feb 12 '16 at 22:01
  • 3
    @SergeyA if you notice, the c++ implementation of accumulate takes in 3 arguments in addition to the joining method, so you just join from the second element to the last element and have the first element as the base element. It would work just fine this way. In fact, separating something with a delimiter is literally one of the examples in the link. – Kevin W. Feb 12 '16 at 22:01
  • @KevinW., nope - because it won't work with empty containers or containers of one element. – SergeyA Feb 12 '16 at 22:03
  • 1
    @SergeyA: I agree you can't call `std::accumulate` when your container is empty, but one element should be just fine (the single element gets passed as `init`, and `begin == end` initially, resulting in no folds) – Ben Voigt Feb 13 '16 at 16:06
  • @BenVoigt, but the operation won't be performed on this element! – SergeyA Feb 13 '16 at 18:07
  • @SergeyA: Naturally not, because if there is only one element, it is the last! – Ben Voigt May 05 '19 at 03:34

12 Answers12

83

My way (without additional branch) is:

const auto separator = "WhatYouWantHere";
const auto* sep = "";
for(const auto& item : items) {
    std::cout << sep << item;
    sep = separator;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • This is of course the more obvious, simpler and cleaner way – edc65 Feb 12 '16 at 23:12
  • Ridiculously concise and elegant. The type of code I hope to write one day. – pyj Feb 19 '16 at 02:01
  • 4
    Yes, it's simple and clever. I can well imagine to use this method the next time I have this kind of task. But then, it comes at the cost of introducing an additional (mutable) variable and assigning a value to it at every iteration. – Frank Puffer Feb 20 '16 at 09:47
  • 2
    If somebody was using this in a context where `separator` needed to be an `std::string` instead of a `char const*`, directly transplanting this pattern would incur expensive copy each iteration. That could be avoided as follows: also have an empty constant `std::string`, make `sep` a pointer to that first, output `*sep`, and finally make `sep` a pointer to `separator` before the next iteration. – underscore_d Jul 19 '17 at 08:52
26

Excluding an end element from iteration is the sort of thing that Ranges proposal is designed to make easy. (Note that there are better ways to solve the specific task of string joining, breaking an element off from iteration just creates more special cases to worry about, such as when the collection was already empty.)

While we wait for a standardized Ranges paradigm, we can do it with the existing ranged-for with a little helper class.

template<typename T> struct trim_last
{
    T& inner;

    friend auto begin( const trim_last& outer )
    { using std::begin;
      return begin(outer.inner); }

    friend auto end( const trim_last& outer )
    { using std::end;
      auto e = end(outer.inner); if(e != begin(outer)) --e; return e; }
};

template<typename T> trim_last<T> skip_last( T& inner ) { return { inner }; }

and now you can write

for(const auto& item : skip_last(items)) {
    cout << item << separator;
}

Demo: http://rextester.com/MFH77611

For skip_last that works with ranged-for, a Bidirectional iterator is needed, for similar skip_first it is sufficient to have a Forward iterator.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • What does `e - (b != e)` mean? Does it imply conversion of bool true to 1? This is not guaranteed. – SergeyA Feb 12 '16 at 22:09
  • 1
    @SergeyA: Means that an input list of zero elements creates an output of zero elements, instead of breaking. – Ben Voigt Feb 12 '16 at 22:09
  • 4
    @SergeyA: Of course that's guaranteed, has been ever since the first time C was standardized. See **[conv.prom]** for the current C++ wording: " A prvalue of type `bool` can be converted to a prvalue of type `int`, with `false` becoming zero and `true` becoming one." – Ben Voigt Feb 12 '16 at 22:10
  • 6
    OP wants a `join` in fact, not skipping last element. (just the last separator should be kept). – Jarod42 Feb 12 '16 at 22:12
  • I somehow missed it. It really is guaranteed! – SergeyA Feb 12 '16 at 22:12
  • @Jarod42: You could indeed write a `join` function using this `skip_last` (or corresponding `skip_first`), but this is far more powerful – Ben Voigt Feb 12 '16 at 22:13
  • I also agree with @Jarod42. While you can add code to take care of the last element, that would be another `if` statement to care for empty containers - and the whole thing looses beauty. – SergeyA Feb 12 '16 at 22:16
  • This is the most elegant and flexible approach, I think. You stick a bunch of these helpers into a library and no one cares what's written inside them, so long as it performs reasonably well. The goal here seems to be ease of use and elegance. I personally have never looked at the code for any library's `std::transform()`, but I've used it plenty and it was more elegant and expressive than pushing to a container in a loop. – Yam Marcovic Feb 12 '16 at 22:19
  • @YamMarcovic, if you are to incapsulate this into library `join`, you can as well right this function with an if inside it - and no need for skip_last. – SergeyA Feb 12 '16 at 22:23
  • 3
    @SergeyA: Sure, it is not the only way to solve concatenation without trailing separator. OP has an X-Y problem. But the question that OP asked, "for each except the last", is interesting and useful, and this answer provides that. – Ben Voigt Feb 12 '16 at 22:26
  • @BenVoigt, it is definitely not XY problem! The need to write values with comas in between them is omnipresent. I myself just recently had to do this :) And the lack of (algorithmic) join is frustrating. And unfortunately, your code does not solve the actual problem, instead, it solves another problem, and while your solution can be used for the actual problem, it is not complete and does not have benefits in this scenario. – SergeyA Feb 12 '16 at 22:29
  • 1
    @SergeyA: It is XY problem. X is "write values with commas between them", Y is "stop the loop one item early". Your comments that you can do X without Y are correct... but Y is still useful for other problems and the technique is good to share. My code solves the problem asked about, namely "for each (ranged for loop) except the last". – Ben Voigt Feb 12 '16 at 22:30
  • Who said anything about 'stopping the loop one item early' but you? – SergeyA Feb 12 '16 at 22:33
  • @SergeyA: Did you ever read the title of the question? – Ben Voigt Feb 12 '16 at 22:34
  • The first name of the title has nothing to do with the actual question, so I kinda mentally skipped it. But yes, taken literally, your answer has merits - but I still believe it does not go with the spirit of the question. – SergeyA Feb 12 '16 at 22:36
  • @SergeyA: I added a parenthetical challenging the XY problem in the question, does that help? – Ben Voigt Feb 12 '16 at 22:40
  • Yes, I think it is clearer now. – SergeyA Feb 12 '16 at 23:12
  • @Deduplicator: Putting the definitions out-of-class would have required a lot more `template ` cruft. Thanks for removing the iterator subtraction that needs random-access. – Ben Voigt Feb 12 '16 at 23:25
  • @BenVoigt: I think you'd better make your trim last have `cbegin()` and `cend()` rather than `begin()` and `end()`. or maybe both. After all, trim_last would likely be used more for reading than for updating. – einpoklum Feb 13 '16 at 10:33
  • 2
    @einpoklum: You could make a good argument that ranged-for in general is used more for reading than updating... yet the Standard committee chose to allow write access to the elements (not to the container) and leave it up to the programmer to choose read-only access (by-value iteration variable, or const reference) or write (non-const reference). My solution preserves that feature. – Ben Voigt Feb 13 '16 at 15:21
  • See https://stackoverflow.com/questions/25652505/skip-1st-iteration-of-range-based-for-loops for a way to simulate ranges – rwst Sep 28 '17 at 07:24
25

Do you know Duff's device?

int main() {
  int const items[] = {21, 42, 63};
  int const * item = items;
  int const * const end = items + sizeof(items) / sizeof(items[0]);
  // the device:
  switch (1) {
    case 0: do { cout << ", ";
    default: cout << *item; ++item; } while (item != end);
  }

  cout << endl << "I'm so sorry" << endl;
  return 0;
}

(Live)

Hopefully I didn't ruin everyone's day. If you don't want to either then never use this.

(mumble) I'm so sorry ...


The device handling empty containers (ranges):

template<typename Iterator, typename Fn1, typename Fn2>
void for_the_device(Iterator from, Iterator to, Fn1 always, Fn2 butFirst) {
  switch ((from == to) ? 1 : 2) {
    case 0:
      do {
        butFirst(*from);
    case 2:
        always(*from); ++from;
      } while (from != to);
    default: // reached directly when from == to
      break;
  }
}

Live test:

int main() {
  int const items[] = {21, 42, 63};
  int const * const end = items + sizeof(items) / sizeof(items[0]);
  for_the_device(items, end,
    [](auto const & i) { cout << i;},
    [](auto const & i) { cout << ", ";});
  cout << endl << "I'm (still) so sorry" << endl;
  // Now on an empty range
  for_the_device(end, end,
    [](auto const & i) { cout << i;},
    [](auto const & i) { cout << ", ";});
  cout << "Incredibly sorry." << endl;
  return 0;
}
Community
  • 1
  • 1
Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
  • how is this `foreach`? – phuclv Feb 13 '16 at 00:53
  • 3
    It's not `foreach` but doing something *for each* element. A loop's a loop, most of the time. – Daniel Jour Feb 13 '16 at 01:04
  • 7
    I could've lived a long and blissful life without ever knowing of this. Thank you. – Bilal Akil Feb 13 '16 at 01:56
  • The complexity of the emotional response that this answer provokes... – Yam Marcovic Feb 13 '16 at 09:11
  • 2
    I _did_ know about Duff's device, but many thanks for showing that it applies here. It is [an elegant tool for a more civilized age](http://tvtropes.org/pmwiki/pmwiki.php/Main/ElegantWeaponForAMoreCivilizedAge)... ok, maybe a less-civilized age. – einpoklum Feb 13 '16 at 10:26
  • 2
    The funniest thing is that the boolean flag approach effectively is optimized into **the device** by gcc. – SergeyA Feb 13 '16 at 13:08
  • The problem with this particular **device** is incorrect handling of empty containers. One extra branch is needed. – SergeyA Feb 13 '16 at 13:21
  • @SergeyA cool, I didn't know that. What I'd be guessing is that the device is rather hard for the optimiser to do loop unrolling. Do you know whether this is true? – Daniel Jour Feb 13 '16 at 13:24
  • @SergeyA To handle the empty case using `container.size()` with appropriate targets in the switch should work. – Daniel Jour Feb 13 '16 at 13:27
  • WOW!! This is a time that some code lines makes me have a heart attack. – dev-masih Feb 13 '16 at 13:42
  • @DanielJour, I do not immediately see how you can handle empty size in switch. Care to edit your answer to include the code? – SergeyA Feb 13 '16 at 13:52
  • @DanielJour, in my scenarios, neither compiler attempted a loop unrolling even with straightforward loop. So I think even less so with **the device**. It is also interesting that of gcc, clang and ICC only gcc recognized the nature of the flag and removed it. 2 other compilers obediently used a register and a branch. Which is just an extra score for gcc. – SergeyA Feb 13 '16 at 13:58
  • @Sergey: Replace the switch guts with `while (1) { cout << ", "; case 1: if (item == end) break; cout << *item; }` – Ben Voigt Feb 13 '16 at 15:26
  • @BenVoigt, this is a branch I was talking about. I though, Daniel meant branchless fix. – SergeyA Feb 13 '16 at 16:29
  • @SergeyA I added an example of how I'd modify the device to handle empty ranges (i.e. empty containers). – Daniel Jour Feb 13 '16 at 17:39
  • Daniel, this is the branch ) – SergeyA Feb 13 '16 at 18:08
  • @SergeyA Ah, you meant with only a single branch all over, I thought you meant without additional branch in the loop. I'd guess without using (possibly costly) assignments this won't be possible. (I tried some manual goto foo, but got sick of it :D ) – Daniel Jour Feb 13 '16 at 18:22
  • 5
    What not a simple `goto`? – Carsten S Feb 13 '16 at 21:18
13

I don't know of any special idioms for this. However, I prefer to special case the first and then perform the operation on the remaining items.

#include <iostream>
#include <vector>

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

    std::cout << "\"";
    if (!values.empty())
    {
        std::cout << values[0];

        for (size_t i = 1; i < values.size(); ++i)
        {
            std::cout << ", " << values[i];
        }
    }
    std::cout << "\"\n";

    return 0;
}

Output: "1, 2, 3, 4, 5"

James Adkison
  • 9,412
  • 2
  • 29
  • 43
13

Typically I do it the opposite way:

bool first=true;
for(const auto& item : items) {
    if(!first) cout<<separator;
    first = false;
    cout << item;
}
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • 2
    You need an if/else to make that work – Ben Voigt Feb 12 '16 at 21:58
  • @BenVoigt, why? It works perfectly well without else in this case. However, it has an unpleasant branch on every iteration - in extreme cases can be a performance hit (can't imagine any real ones, though). – SergeyA Feb 12 '16 at 22:02
  • @SergeyA: well predicted branches are essentially free (and this one is perfectly predicted after a few iterations), I wouldn't worry about it. – Matteo Italia Feb 12 '16 at 22:03
  • 2
    first will never go false – Christophe Feb 12 '16 at 22:04
  • MatteoItalia, neither would I. I was making a general statement. It also has unpleasant assignment. – SergeyA Feb 12 '16 at 22:05
  • @Cristophe: ops, forgot the assignment in the braces, sorry, fixed. – Matteo Italia Feb 12 '16 at 22:06
  • @SergeyA: disputable; at low level it's essentially free too, and compared to all other solutions I find this both easier to write and less visually cluttered. But that's of course a stylistical matter. – Matteo Italia Feb 12 '16 at 22:10
  • I disagree with being 'free'. If compiler optimizes it to register storage, it will be free. But if not, on cache coherent systems it would be rather expensive memory write. – SergeyA Feb 12 '16 at 22:19
  • @SergeyA It doesn't even need to be in a register. Compiler can actually make the first one a special case and take it out of the loop. – Yam Marcovic Feb 12 '16 at 22:51
  • @YamMarkovic, it might, but not necessarily will. – SergeyA Feb 12 '16 at 23:14
  • @SergeyA I would be surprised if the compiler didn't optimize away that flag. – Deduplicator Feb 12 '16 at 23:40
  • 2
    @Deduplicator, I tested it on Clang and gcc. gcc completely removes the flag altogether - just orders jmps to make sure it is called only once, but clang goes by the book, checking it on every iteration (puts in register in my test, though). – SergeyA Feb 13 '16 at 01:15
7

I like plain control structures.

if (first == last) return;

while (true) {
  std::cout << *first;
  ++first;
  if (first == last) break;
  std::cout << separator;
}

Depending on your taste, you can put the increment and test in a single line:

...
while (true) {
  std::cout << *first;
  if (++first == last) break;
  std::cout << separator;
}
filipos
  • 645
  • 6
  • 12
6
int a[3] = {1,2,3};
int size = 3;
int i = 0;

do {
    std::cout << a[i];
} while (++i < size && std::cout << ", ");

Output:

1, 2, 3 

The goal is to use the way && is evaluated. If the first condition is true, it evaluates the second. If it is not true, then the second condition is skipped.

sudo rm -rf slash
  • 1,156
  • 2
  • 16
  • 27
5

I don't thing you can get around having a special case somewhere... For example, Boost's String Algorithms Library has a join algorithm. If you look at its implementation, you'll see a special case for the first item (no proceeding delimitier) and a then delimiter is added before each subsequent element.

Mark Waterman
  • 961
  • 7
  • 16
  • 2
    Have a look at [ostream joiners](http://en.cppreference.com/w/cpp/experimental/ostream_joiner). – einpoklum Feb 12 '16 at 22:50
  • @einpoklum thanks, I didn't know that was coming. Reinforces that there's no algorithm(/iterator) will get around having a special case: for ostream_joiner it's embodied in the boolean that tracks whether you're about to write the first element. Basic thesis is that when you're joining N elements this way (where N > 1), you're not doing the same operation N times--you've got one element that must be handled differently. – Mark Waterman Feb 12 '16 at 23:15
  • 1
    Ok, yes, but it's not the coding using those joiner that has to track anything. See also @MaartenHilferink 's [answer](http://stackoverflow.com/a/35377869/1593077). The principle of "the best line of code is the one you don't have to write yourself", as others have quipped here. – einpoklum Feb 13 '16 at 10:24
5

You could define a function for_each_and_join that takes two functors as argument. The first functor does work with each element, the second works with each pair of adjacent elements:

#include <iostream>
#include <vector>

template <typename Iter, typename FEach, typename FJoin>
void for_each_and_join(Iter iter, Iter end, FEach&& feach, FJoin&& fjoin)
{
    if (iter == end)
        return;

    while (true) {
        feach(*iter);
        Iter curr = iter;
        if (++iter == end)
            return;
        fjoin(*curr, *iter);
    }
}

int main() {
    std::vector<int> values = { 1, 2, 3, 4, 5 };
    for_each_and_join(values.begin(), values.end()
    ,  [](auto v) { std::cout << v; }
    ,  [](auto, auto) { std::cout << ","; }
    );
}

Live example: http://ideone.com/fR5S9H

  • You really can do without the first function. The concept of a *monoid* lets you kick things off with a special empty value for the "left" and the first real element for "right". – JDługosz Feb 13 '16 at 20:02
3

I don't know about "idiomatic", but C++11 provides std::prev and std::next functions for bidirectional iterators.

int main() {
    vector<int> items = {0, 1, 2, 3, 4};
    string separator(",");

    // Guard to prevent possible segfault on prev(items.cend())
    if(items.size() > 0) {
        for(auto it = items.cbegin(); it != prev(items.cend()); it++) {
            cout << (*it) << separator;
        }
        cout << (*prev(items.cend()));
    }
}
pyj
  • 1,489
  • 11
  • 19
  • Way way too many lines of code my friend :-) ... and I don't want to have to check the size of `items` either. – einpoklum Feb 12 '16 at 22:11
  • 2
    @einpoklum, I agree with your sentiment. "The best line of code is one you don't have to write." I was just trying to give you something using new C++11 fancy tools. – pyj Feb 12 '16 at 22:21
3

Heres a little trick I like to use:

For bi-directionally iterable objects: for ( auto it = items.begin(); it != items.end(); it++ ) { std::cout << *it << (it == items.end()-1 ? "" : sep); };

Using the ternary ? operator I compare the current position of the iterator against the item.end()-1 call. Since the iterator returned by item.end() refers to the position after the last element, we decrement it once to get our actual last element.

If this item isn't the last element in the iterable, we return our separator (defined elsewhere), or if it is the last element, we return an empty string.

For single direction iterables (tested with std::forward_list): for ( auto it = items.begin(); it != items.end(); it++ ) { std::cout << *it << (std::distance( it, items.end() ) == 1 ? "" : sep); };

Here, we're replacing the previous ternary condition with a call to std::distance using the current iterator location, and the end of the iterable.

Note, this version works with both bidirectional iterables as well as single direction iterables.

EDIT: I realize you dislike the .begin() and .end() type iteration, but if you're looking to keep the LOC count down, you're probably going to have to eschew range based iteration in this case.

The "Trick" is simply wrapping the comparison logic within a single ternary expression, if your comparison logic is relatively simple.

WeRelic
  • 290
  • 1
  • 12
  • I don't really see where the trick is. Also, you assume items is bidirectionally iterable. – einpoklum Feb 13 '16 at 20:35
  • Edited to account for single direction iterables. Your question didn't specify them as a constraint, but it's worth accounting for. The second example will work with both. – WeRelic Feb 13 '16 at 21:49
  • 1
    In the single-direction iterables example, won't it run at O(n^2) complexity? I'd imagine that `std::distance` has not way to know the answer other than iterating all the way to end, and you're doing it in every loop iteration. – Jonathan Feb 14 '16 at 07:52
  • @Jonathan True, it's not the most efficient. I suppose it could be improved by storing the length of the iterable in an int before the loop and converting the iterator position to an int type somehow then comparing the values. That would bring it closer to O(n), right? (Sorry if I'm off, I'm not great with big O notation, it's something I'm working on) – WeRelic Feb 15 '16 at 03:37
3

I like the boost::join function. So for more general behavior, you want a function that is called for each pair of items and can have a persistent state. You'd use it as a funcgion call with a lambda:

foreachpair (range, [](auto left, auto right){ whatever });

Now you can get back to a regular range-based for loop by using range filters!

for (auto pair : collection|aspairs) {
    Do-something_with (pair.first);
}

In this idea, pair is set to a pair of adjecent elements of the original collection. If you have "abcde" then on the first iteration you are given first='a' and second='b'; next time through first='b' and second='c'; etc.

You can use a similar filter approach to prepare a tuple that tags each iteration item with an enumeration for /first/middle/last/ iteration and then do a switch inside the loop.

To simply leave off the last element, use a range filter for all-but-last. I don't know if that's already in Boost.Range or what might be supplied with Rangev3 in progress, but that's the general approach to making the regular loop do tricks and making it "neat".

JDługosz
  • 5,592
  • 3
  • 24
  • 45