7

Let's say that we have an array with n elements (n > 0).

We would like to output a list of those elements, with a separator between them.

A common approach to this problem is:

foreach item
  (
    output item
    output separator
  )
trim last separator

But it seems a bit messy to have to do that.

Another approach would be:

check that there is at least one element
loop
  (
     output element
     next element, or break if no more elements
     output separator
  )

But I am not sure that it will always work.

Do you see other clever ways to do that, for example in C, C++?

Benoit
  • 76,634
  • 23
  • 210
  • 236

11 Answers11

16
char *sep = "";
for (i = 0; i < size; ++i) {
    printf("%s%s", sep, item[i]);
    sep = ", ";
}
Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
11
for (i = 0; i < n; ++i) switch(i) {
    default: output_separator();
    case 0: output_item(i);
}

or variations on this. I can't really think of how else not to repeat output_item(i).

lijie
  • 4,811
  • 22
  • 26
  • Missing a `case` before that `0` – Chris Lutz Nov 24 '10 at 10:57
  • This looks like it will output 0 and then n-1 separators. – Geoffrey Nov 24 '10 at 10:59
  • 2
    @Geoffrey - In C, `switch` has implicit fallthrough. You have to specifically `break` to prevent fallthrough. – Chris Lutz Nov 24 '10 at 11:03
  • 2
    Akin too, but less violating than, Duff's device (http://en.wikipedia.org/wiki/Duff%27s_device). Paraphrasing, this deserves a vote, but I'm not sure whether up or down (I went with up). – mtrw Nov 24 '10 at 11:27
  • @lijie - I totally did not just write a generic version of this that takes a pointer to a printing function and a `void *`. And two `size_t`s for the array length and element size. – Chris Lutz Nov 24 '10 at 11:30
  • @Chris: that would be a totally instructive exercise :) – lijie Nov 24 '10 at 13:24
5

Sometimes:

output item 0
for item 1 to n
{
    output separator
    output item
}

is shorter.

Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
5

As it is tagged as language-agnostic I think it's important to point out, that some languages have built-in features to spare you from even thinking about this problem. Take this python code for example:

>>> print string.join(['list', 'of', 'some', 'words'], ', ')
list, of, some, words
Frank
  • 10,461
  • 2
  • 31
  • 46
  • 1
    I think his question is akin to asking how the join-method is implemented. – Geoffrey Nov 24 '10 at 11:14
  • 1
    @Geoffrey: I disagree, it's language agnostic so the join solution should not only be included but highlighted. I can't tell you how often I see newbies to high level languages try to do the foreach thing without realising that join is the correct way to do this. I used to do it myself. – slebetman Nov 24 '10 at 21:01
  • Oh by the way, in tcl it would be: `puts [join $list ", "]` and I suppose javascript could be: `console.log(array.join(", "))` – slebetman Nov 24 '10 at 21:03
  • objective-c: [array componentsJoinedByString:@", "] – Kenny Winker Nov 25 '10 at 14:00
5

A possible C++ solution:

http://groups.google.com/group/comp.lang.c++/msg/a746a588cedfa44b

Summary: write an infix_ostream_iterator, which is basically the same as ostream_iterator except that the "separator" parameter really is a separator, not a suffix to every item. Usage would then be:

std::copy(first, last, infix_ostream_iterator<ItemType>(output, separator));
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
5

For better or worse, I use counted loops, with

for (i = 0; i < num_items; i++){
  if (i > 0) output separator;
  output item[i];
}

I'm sure this is downvote-bait for being old fashioned, but it works.

If anybody wants to tell me it's inefficient, boy do I have a flame ready ;-)

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
3

This version avoids any additional branches:

int i = 0;
goto skip_delim;
do {
               put_delim();
   skip_delim: put_el(i++);
} while (i < size);

(For those who are afraid of goto, it can be written using approach from Duff's device)

ruslik
  • 14,714
  • 1
  • 39
  • 40
  • +2 for mentioning Duff's device, -1 for not actually using switch :) – dietbuddha Nov 25 '10 at 05:46
  • @diebuddha the switch have to be outside the loop, not inside like in lijie 's example. If you think it's better than a goto, feel free to use it :) Interesting reading: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html – ruslik Nov 25 '10 at 07:43
3

The "first check" idiom in Haskell:

intersperse :: Show a => String -> [a] -> String
intersperse _ [] = ""
intersperse s (x:xs) = show x ++ concatMap ((s ++) . show) xs

It's used like so:

*Main> intersperse "," [1,2,3]
"1,2,3"
*Main> intersperse "," [1]
"1"
*Main> intersperse ";" [1,2]
"1;2"
*Main> intersperse "," []
""
keiter
  • 3,534
  • 28
  • 38
2

I always use first item check idiom. Here is the code in java:

List<Object> list;
if (list.size() > 0) {
   put(list.get(0));
}
for(int i = 1; i < list.size(); i++) {
   putSeparator();
   put(list.get(i));                                
}
Vladimir Ivanov
  • 42,730
  • 18
  • 77
  • 103
1
for i in items.length-1; do
   output item; output separator
output last item
khachik
  • 28,112
  • 9
  • 59
  • 94
1

In Common Lisp, it's bordering on simple, as long as you can hard-code the separator.

(defun return-delimited-list (list &optional stream)
  (format stream "~{~A~^, ~}" list))

When called, this returns a string consisting of the elements in list, separated by ", " (except for the last element not being followed by anything). Well, if fed an output stream, it prints it to the stream, it just so happens that 'nil' means "No stream, just return a string".

Vatine
  • 20,782
  • 4
  • 54
  • 70