25
\$\begingroup\$

What are some clever (brief and idiomatic) approaches to taking a list of strings and returning a single properly punctuated string built from the list, with each element quoted.

This came up for me while experimenting with Groovy, for which my too-literal, but illustrative solution is

def temp = things.collect({"\'${it}\'"})
switch (things.size()) {
    case 1:
        result = temp[0]
        break
    case 2:
        result = temp.join(" and ")
        break
    default:
        result = temp.take(temp.size()-1).join(", ") + ", and " + temp[-1]
        break
}

That is, ['1'] should yield '1', ['1','2'] should yield '1 and 2', [see what I did there?] and ['1','2','3'] should yield '1, 2, and 3'.

I have some good answers for Groovy, but I'd like to see what other languages can do.

What are some compact clever approaches in various languages that take advantage of the features and idioms of those languages?

\$\endgroup\$
12
  • 7
    \$\begingroup\$ Welcome to PPCG. Generally questions posted here are challenges to the community. As such they need an objective winning criteria. I believe this question maps reasonably well to being a code-golf challenge. Can you tag it as such? If so, I think you should tighten up the input and output specifications a bit. \$\endgroup\$ Commented Sep 11, 2014 at 18:58
  • 8
    \$\begingroup\$ This would be more interesting with real sentences: ['we invited the stripper','JFK','Stalin'] \$\endgroup\$ Commented Sep 11, 2014 at 21:06
  • 1
    \$\begingroup\$ Can we assume that the strings themselves don't contain commas already? \$\endgroup\$ Commented Sep 11, 2014 at 21:16
  • 2
    \$\begingroup\$ Challenge should have been titled "Who gives a ---- about an Oxford comma?" \$\endgroup\$ Commented Sep 12, 2014 at 7:14
  • 3
    \$\begingroup\$ @OldCurmudgeon I think you mean "Americanized rubbish" ;) \$\endgroup\$ Commented Sep 21, 2014 at 18:59

39 Answers 39

77
\$\begingroup\$

CSS, 132 116 115 bytes

a:not(:last-child):nth-child(n+2):after,a:nth-last-child(n+3):after{content:","}a+:last-child:before{content:"and "
<p>
  <a>one</a>
</p>
<p>
  <a>one</a>
  <a>two</a>
</p>
<p>
  <a>one</a>
  <a>two</a>
  <a>three</a>
</p>
<p>
  <a>one</a>
  <a>two</a>
  <a>three</a>
  <a>four</a>
</p>

a:not(:last-child):nth-child(n+2):after,a:nth-last-child(n+3):after{content:","}a+:last-child:before{content:"and "

CSS is not seen too often in code golf because it can only format text, but it actually works for this challenge and I thought it would be fun to do. See it in action using the snippet above (click "Show code snippet").

List should be in a linked HTML file with each element surrounded by <a> tags and separated by line breaks. The list items should be the only elements in their parent element, e.g.

<a>one</a>
<a>two</a>
<a>three</a>

Explanation

a:not(:last-child):nth-child(n+2)::after,
a:nth-last-child(n+3)::after {
    content: ",";
}

a + :last-child::before {
    content: "and ";
}

Let's consider the ungolfed version above. If you're not familiar with how CSS works, everything outside the curly braces is a selector that determines the set of HTML elements to which the declarations inside the braces apply. Each selector-declaration pair is called a rule. (It's more complicated than that but will suffice for this explanation.) Before any styling is applied, the list appears separated by only spaces.

We want to add commas after every word except the last, except for two-word lists, which get no commas. The first selector, a:not(:last-child):nth-child(n+2):after, selects all elements except the first and the last. :nth-child(n+2) is a shorter way of saying :not(:first-child), and it basically works by selecting elements whose index (starting at 1) is greater than or equal to 2. (Yes, it still confuses me a little. The MDN docs might help.)

Now we just need to select the first element to get a comma if there are three or more elements total. a:nth-last-child(n+3):after works like :nth-child, but counting from the back, so it selects all elements except the last two. The comma takes the union of the two sets, and we use the :after pseudo-element to add content immediately after each selected element.

The second rule is easier. We need to add "and" before the last element in the list, unless it is a single element. In other words, we need to select the last element that is preceded by another element. + is the adjacent sibling selector in CSS.

\$\endgroup\$
8
  • 1
    \$\begingroup\$ Brilliant! I love it. :D \$\endgroup\$
    – COTO
    Commented Sep 11, 2014 at 22:28
  • \$\begingroup\$ If only you'd use <li>. \$\endgroup\$
    – slebetman
    Commented Sep 12, 2014 at 4:01
  • 1
    \$\begingroup\$ <li> would be ideal, but that would add two extra characters to the selectors. I chose <a> because it is one letter and doesn't apply its own formatting. \$\endgroup\$ Commented Sep 12, 2014 at 4:07
  • \$\begingroup\$ Wow... this is very impressive. Cleverness award. \$\endgroup\$
    – user16402
    Commented Sep 12, 2014 at 18:38
  • \$\begingroup\$ CSS as the top answer? Nice. \$\endgroup\$
    – Brandon
    Commented Sep 12, 2014 at 19:54
13
\$\begingroup\$

Haskell: 81, 77 74 chars

f[x]=x
f[x,y]=x++" and "++y
f[x,y,z]=x++", "++f[y++",",z]
f(x:y)=x++", "++f y

Haskell features: Pattern matching

\$\endgroup\$
11
  • \$\begingroup\$ You can remove some spaces \$\endgroup\$
    – Ray
    Commented Sep 12, 2014 at 18:19
  • 1
    \$\begingroup\$ You could just remove f[x,y,z] \$\endgroup\$
    – seequ
    Commented Sep 12, 2014 at 18:37
  • \$\begingroup\$ And now, it is pretty much standard recursion. :) \$\endgroup\$
    – seequ
    Commented Sep 12, 2014 at 18:40
  • \$\begingroup\$ you forgot the oxford comma and spaces after the commas - f["a","b","c"] is supposed to be "a, b, and c" but it is "a,b and c" \$\endgroup\$ Commented Sep 12, 2014 at 18:49
  • 4
    \$\begingroup\$ how about golfing by replacing y++", and "++z by f[y++",",z] :) \$\endgroup\$ Commented Sep 12, 2014 at 19:19
7
\$\begingroup\$

Ruby, 47 bytes

q=" and ";p$*[2]?(a=$*.pop;$**", "+?,+q+a):$**q

Explanation

  • The input are the command line arguments ($*).
  • When $* has a third element ($*[2] does not return nil), take all elements minus the last one and turn them into a comma-separated String using Array#*. Finally add an extra comma, the string " and " and the last command line argument.
  • When $* has no third element, two, one, or zero arguments were given. The arguments can safely be joined with the string " and " and produce the correct result.
\$\endgroup\$
0
6
\$\begingroup\$

Python 2 (61)

s=input()
d=s.pop()
print", ".join(s)+", and "[8-7*len(s):]+d

The main trick is to cut off part of the final joiner ", and " for one and two elements. For one, all of it is cut out, and for two, the comma is removed. This is done by slicing out [8-7*len(s):] (noting that s is one shorter after the pop).

Unfortunately, d cannot just be replaced with its expression or the pop would happen too late.

\$\endgroup\$
3
  • \$\begingroup\$ You could replace the first s with s=input() and remove the first line, if I'm not mistaken. Saves 2 chars. \$\endgroup\$
    – tomsmeding
    Commented Sep 13, 2014 at 6:33
  • \$\begingroup\$ @tomsmeding I don't understand what you're suggesting. The code is referring to s multiple times. \$\endgroup\$
    – xnor
    Commented Sep 13, 2014 at 7:07
  • \$\begingroup\$ Wait, I do weird stuff this morning. Forget that. :) \$\endgroup\$
    – tomsmeding
    Commented Sep 13, 2014 at 7:16
6
\$\begingroup\$

CSS, 62 chars 112 chars

Inspired by the other entries, even shorter. Note that it requires that A elements are not separated by white spaces:

a+a:before{content:", "}a+a:last-child:before{content:", and "

http://jsfiddle.net/olvlvl/1Ls79ocb/

Fix for the "one and two", as pointed at by Dennis:

a+a:before{content:", "}a+a:last-child:before{content:", and "}a:first-child+a:last-child:before{content:" and "

http://jsfiddle.net/olvlvl/1Ls79ocb/3/

\$\endgroup\$
0
6
\$\begingroup\$

Perl (v 5.10+) - 37 35 34 28

@F>2&&s/ /, /g;s/.* \K/and /

to be run with perl -ape, with the list supplied space separated on STDIN.

Output for various inputs:

$ perl -ape '@F>2&&s/ /, /g;s/.* \K/and /'
oxford
oxford
oxford cambridge
oxford and cambridge
oxford cambridge comma
oxford, cambridge, and comma
oxford cambridge comma space
oxford, cambridge, comma, and space
\$\endgroup\$
8
  • \$\begingroup\$ You can reduce this by 3 bytes by changing your last substitution to s/(\S+)$/and $1/ \$\endgroup\$ Commented Sep 17, 2014 at 22:19
  • 1
    \$\begingroup\$ @ThisSuitIsBlackNot You can drop the '\n' (thanks), but you can't drop the spaces, or an input of "x" becomes "and x" \$\endgroup\$
    – abligh
    Commented Sep 18, 2014 at 6:41
  • \$\begingroup\$ Oops, should have tested with more inputs. Anyway, you forgot the $ anchor in your edit, so an input of foo bar baz becomes foo, and bar, baz. Also, you can remove one of the spaces by doing s/( \S+)$/ and$1/ \$\endgroup\$ Commented Sep 18, 2014 at 14:21
  • \$\begingroup\$ My last comment still leaves you at 35, but you can write $#F>1 as @F>2 to shave off one more. Sorry for all the suggestions for micro improvements, I just really like this answer :) \$\endgroup\$ Commented Sep 18, 2014 at 15:14
  • \$\begingroup\$ @ThisSuitIsBlackNot not at all - microedits welcome. I am secretly competing with 28 bytes of CJam and 30 bytes of Golfscript. However I did get to 34 without doing the translation of your comment before last (tested with echo -n ... | wc -c). If you miss the space before (\S+) you'd get to 33 characters, but if you put in test this you will get test and this (two spaces after test), so without more I think that's wrong. \$\endgroup\$
    – abligh
    Commented Sep 18, 2014 at 17:12
4
\$\begingroup\$

Javascript (63)

l=a.length;l>2&&(a[l-1]='and '+a[l-1]);a.join(l>2?', ':' and ')

Cases:

  • a = [1] => 1
  • a = [1, 2] => 1 and 2
  • a = [1, 2, 3] => 1, 2, and 3

Caveat: this will modify the last element in an array with length > 2.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Nice use of && \$\endgroup\$ Commented Sep 12, 2014 at 20:04
4
\$\begingroup\$

GNU sed - 69 chars including 1 for -r flag

s/ /, /g
s/( [^ ]+)( [^ ]+)$/\1 and\2/
s/^([^,]+),([^,]+)$/\1 and\2/

Takes a space-separated list (fairly idiomatic for shell scripts).

Example

$ sed -r -f oxfordcomma.sed <<< "1"
1
$ sed -r -f oxfordcomma.sed <<< "1 2"
1 and 2
$ sed -r -f oxfordcomma.sed <<< "1 2 3"
1, 2, and 3
$
\$\endgroup\$
3
  • \$\begingroup\$ Yes, let's go with code-golf. \$\endgroup\$
    – orome
    Commented Sep 11, 2014 at 19:24
  • \$\begingroup\$ The second output should be 1 and 2 instead of 1, 2 \$\endgroup\$
    – Optimizer
    Commented Sep 11, 2014 at 19:40
  • \$\begingroup\$ @Optimizer - quite right. Fixed. \$\endgroup\$ Commented Sep 11, 2014 at 20:11
3
\$\begingroup\$

Python 2 - 71, 70 68

s=input()
l=len(s)-1
print', '.join(s[:l])+', and '[l<2:]*(l>0)+s[l]

Character count including both, input and print.

\$\endgroup\$
3
\$\begingroup\$

Ruby, 57 bytes

f=->l{s=l*', ';s.sub(/,(?!.*,)/,(l.size<3?'':?,)+' and')}

I'm joining the string with , and then I'm replacing the last comma in the string with an and (and optional comma depending on list length).

\$\endgroup\$
3
\$\begingroup\$

Cobra - 60

do(l as String[])=l.join(', ',if(l.length<3,'',',')+' and ')

Cobra's List<of T>.join function lets you specify a different separator for the final two elements of the list, which is what makes this so short.

\$\endgroup\$
3
\$\begingroup\$

Perl - 59

sub f{$a=join', ',@_;$#_&&substr$a,(@_>2)-3,@_<3,' and';$a}

Joins the list with commas, then if the list has more than one element, either adds ' and' after the last comma (if length >= 3), or replaces the last comma with it (if length == 2).

\$\endgroup\$
3
\$\begingroup\$

PHP, 192 167 146 136 characters:

$n=' and ';$s=count($a);echo ($s<3)?join($n,$a):join(', ',array_merge(array_slice($a,0,$s-2),Array(join(",$n",array_slice($a,-2,2)))));

Based on a function I wrote years ago at http://www.christopherbloom.com/2011/05/21/join-implode-an-array-of-string-values-with-formatting/

\$\endgroup\$
4
  • \$\begingroup\$ Can you please put your code (with Oxford commas) into the answer text? \$\endgroup\$
    – user16402
    Commented Sep 12, 2014 at 18:36
  • \$\begingroup\$ Yes, sorry. I was on my phone and it wouldn't paste the code properly. I'll update from my desktop \$\endgroup\$ Commented Sep 12, 2014 at 18:37
  • \$\begingroup\$ @Chrisbloom7 even if it is trivial to add, the answer should include a working code, not one that can be made to be one. also, because the objective is to make the code as smaller as possible, not posting working code makes your score non existing. \$\endgroup\$ Commented Sep 12, 2014 at 18:45
  • \$\begingroup\$ Apologies again. First time poster at CG. I've fixed my answer \$\endgroup\$ Commented Sep 12, 2014 at 19:06
3
\$\begingroup\$

Batch - 151 Bytes

@echo off&set f=for %%a in (%~1)do
%f% set/aa+=1
%f% set/ac+=1&if !c!==1 (set o=%%a)else if !c!==!a! (set o=!o!, and %%a)else set o=!o!, %%a
echo !o!

Note; you have to call the script from cmd with the /v switch set as on, this is so I don't have to include the lengthy setLocal enableDelayedExpansion in the script. Otherwise add 30 to the byte count, and call the script normally.

h:\uprof>cmd /von /c test.bat "1 2 3"
1, 2, and 3

h:\uprof>cmd /von /c test.bat "1 2 3 4 5"
1, 2, 3, 4, and 5
\$\endgroup\$
3
\$\begingroup\$

Groovy, 47 43 57 characters , JavaScript ES6 56 characters

Groovy:

(a[1]?a[0..-2].join(", ")+(a[2]?",":"")+" and ":"")+a[-1]

Since the array is filled with characters, we can replace a.size>1 by a[1]

JavaScript, ES6:

a.join(', ').replace(/,([^,]+)$/,`${a[2]?',':''} and$1`)

Both cases assume that variable a has the array in question.

\$\endgroup\$
1
  • 6
    \$\begingroup\$ The Oxford comma is the comma before the conjunction. \$\endgroup\$
    – Dennis
    Commented Sep 11, 2014 at 21:09
3
\$\begingroup\$

PHP, 86 84 chars

$a = ['one', 'two', 'three', 'four', 'five'];

With the array initialized, we start counting:

$L=count($a)-1;$S=', ';$L<2?($S=' and '):($a[$L]='and '.$a[$L]);echo implode($S,$a);

Prettified:

$last_index = count($a) - 1;
$separator = ', ';
if ($last_index < 2) {
    $separator = ' and ';
} else {
    $a[$last_index] = 'and '.$a[$last_index];
}
echo implode($separator, $a);

The last item in the list is modified. This should be OK because in PHP, array assignment and function calls perform copies.

\$\endgroup\$
3
\$\begingroup\$

CJam, 35 30 28 27 bytes

q~)\_"and "L?@+a+_,2=", ">*

This is a program that reads from STDIN and prints to STDOUT. Try it online.

How it works

q~                                     " Q := eval(input())                               ";
  )                                    " P := Q.pop()                                     ";
   \_"and "L?@+                        " P := (Q ? 'and ' : '') + P                       ";
                a+                     " Q += [P]                                         ";
                  _,2=", ">            " J := ', '[(len(Q) == 2):]                        ";
                           *           " R := J.join(Q)                                   ";
                                       " print R (implicit)                               ";

Example run

$ cjam <(echo 'q~)\_"and "L?@+a+_,2=", ">*') <<< '["1"]'; echo
1
$ cjam <(echo 'q~)\_"and "L?@+a+_,2=", ">*') <<< '["1""2"]'; echo
1 and 2
$ cjam <(echo 'q~)\_"and "L?@+a+_,2=", ">*') <<< '["1""2""3"]'; echo
1, 2, and 3
$ cjam <(echo 'q~)\_"and "L?@+a+_,2=", ">*') <<< '["1""2""3""4"]'; echo
1, 2, 3, and 4
\$\endgroup\$
1
  • \$\begingroup\$ I am on your tail - see below :-) \$\endgroup\$
    – abligh
    Commented Sep 18, 2014 at 22:50
3
\$\begingroup\$

Google Sheets, 67 68 67 92 bytes

=SUBSTITUTE(JOIN(", ",FILTER(A:A,A:A<>"")),",",IF(COUNTA(A:A)>2,",","")&" and",COUNTA(A:A)-1

The input starts at cell A1 and continuing down for however many entries exist.
JOIN merges them all into a string with a comma-space between each.
FILTER removes any non-blanks so you don't end up with infinite commas at the end.
SUBSTITUTE replaces the last comma (found by COUNTA counting the non-blank inputs).

It's not very exciting but it's doable in a single cell.

\$\endgroup\$
6
  • \$\begingroup\$ This leaves out the oxford comma. \$\endgroup\$
    – Umbrella
    Commented Oct 6, 2017 at 21:12
  • \$\begingroup\$ @Umbrella Well, that was stupid of me, wasn't it? +1 bytes \$\endgroup\$ Commented Oct 9, 2017 at 13:44
  • \$\begingroup\$ You should be able to drop the terminal ) off of this formula for -1 Byte; Well dang, that was the quickest edit I've ever seen +1 \$\endgroup\$ Commented Oct 9, 2017 at 19:37
  • \$\begingroup\$ Two notes after testing this - A:A>"" should be converted to A:A<>"" and this does not implement omitting the oxford comma on cases of only 2 objects \$\endgroup\$ Commented Oct 9, 2017 at 20:08
  • \$\begingroup\$ @TaylorScott I guess I only ever tested with text and missed the issue with >"". The quickie correction I made earlier clearly wasn't tested. I don't like the direction that went... \$\endgroup\$ Commented Oct 9, 2017 at 20:23
3
\$\begingroup\$

Vyxal, 27 bytes

ṫ$:[`λ¬ `pJ:Ḣ₃[÷ð$Ṡ|‛, j]|_

Try it Online!

ṫ                           # Tail extract (x[:-1], x[-1] )
 $:[                        # If length > 1
    `λ¬ `p                  # Prepend a 'and '
          J                 # Merge
           :Ḣ₃[             # If length = 2
               ÷ð$Ṡ         # Add in a space and join
                   |    ]   # Else...
                    ‛, j    # Join by ', '
                         |_ # Else pop
\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6) 60

Assuming no empty strings in input array

f=(a,b=a.pop())=>a[0]?a.join(', ')+(a[1]?',':'')+' and '+b:b

Test In FireFox/Firebug console

console.log(f(['We invited the stripper','JFK','Stalin']))

Output

 We invited the stripper, JFK, and Stalin
\$\endgroup\$
6
  • \$\begingroup\$ How do I run this? I tried it in node --harmony with an array of strings var a = 'abcdefgh'.split('');, but it just sits there with a ... and appears to do nothing. Also +1 for using unique ES6 feature ()=>. \$\endgroup\$
    – Adrian
    Commented Sep 12, 2014 at 4:51
  • \$\begingroup\$ @Adrian the function returns a string, without any output. I'll add a test in the answer. \$\endgroup\$
    – edc65
    Commented Sep 12, 2014 at 6:41
  • \$\begingroup\$ I think f=(a,b=a.pop())=>a[0]?a.join(', ')+', and '+b:b is also correct and 13 bytes shorter. \$\endgroup\$
    – Ingo Bürk
    Commented Sep 12, 2014 at 14:23
  • 1
    \$\begingroup\$ @IngoBürk that cuts the part that handles the comma having 2 items. Guess what? With 2 items the output is wrong. \$\endgroup\$
    – edc65
    Commented Sep 12, 2014 at 14:30
  • \$\begingroup\$ @edc65 Ah, d'oh. May bad. \$\endgroup\$
    – Ingo Bürk
    Commented Sep 12, 2014 at 15:03
2
\$\begingroup\$

JavaScript - 60 56 71 67 63

Not exactly an idiomatic approach, but I had fun writing it and I like regex.

Assuming the array is stored in var a:

a.join((a.length>2?',':'')+' ').replace(/([^,\s])$/,"and $1")

Shortened by simply checking for index [2]: (yay boolean coercion)

a.join((!!a[2]?',':'')+' ').replace(/([^,\s])$/,'and $1')

Apparently I suck at testing and skipped a single-entry test. Here's the fixed version:

a.join((!!a[2]?',':'')+' ').replace(/([^,\s])$/,(!!a[1]?'and ':'')+'$1')

Shaved off 2 chars by inverting my booleans and 3 more by moving the space from the concatenation into the then/else of the first ternary:

a.join((!a[2]?' ':', ')).replace(/([^,\s])$/,(!a[1]?'':'and ')+'$1')

Thanks to @tomsmeding for reminding me that I don't have to coerce my booleans because JS does that for me. I also realised I forgot to remove the parentheses separating the first ternary from the concatenation inside the join():

a.join(a[2]?', ':' ').replace(/([^,\s])$/,(a[1]?'and ':'')+'$1')

Did I do that right? Obligatory new golfer apology.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Liking regex is known as masochism. \$\endgroup\$
    – seequ
    Commented Sep 12, 2014 at 19:05
  • \$\begingroup\$ Actually you didn't need the !! at all; a[2]?A:B works already. Might have to invert your booleans again. \$\endgroup\$
    – tomsmeding
    Commented Sep 13, 2014 at 6:36
  • \$\begingroup\$ @tomsmeding -- you are correct. I am always wary of JavaScript's default boolean coercion behaviour, so I have the borne-in habit of manually coercing my booleans... \$\endgroup\$
    – Adrian
    Commented Sep 13, 2014 at 7:11
  • \$\begingroup\$ @Adrian Correction: for any value but an empty string, a[i] equates to true. Well, almost. I'm assuming that if you get 2 inputs, your array has length two. Then a[2]===undefined and undefined evaluates to false. EDIT: your edit is what I meant :) \$\endgroup\$
    – tomsmeding
    Commented Sep 13, 2014 at 7:14
2
\$\begingroup\$

Golfscript - 31 39 34 30

Hello, World! I am a long time lurker, first time poster. Here is my 30 byte solution in Golfscript (given the array such as [1 2 3] is already on the stack).

.,3<" and ":A{~A(;\+]", "}if*

The results are proper for all test cases.

EDIT: Take that, CJam!

\$\endgroup\$
4
  • \$\begingroup\$ Edited to account for the length 1 array. \$\endgroup\$ Commented Sep 14, 2014 at 19:39
  • \$\begingroup\$ Edited for a more efficient algorithm. \$\endgroup\$ Commented Sep 15, 2014 at 4:07
  • \$\begingroup\$ The battle continues. :P \$\endgroup\$
    – Dennis
    Commented Sep 15, 2014 at 5:26
  • \$\begingroup\$ Ah, but now we're even. :P \$\endgroup\$ Commented Sep 15, 2014 at 5:59
2
\$\begingroup\$

Xojo, 52 71 83 chars

dim i as int8=ubound(L)
L(i)=if(i=0,"","and ")+L(i)
Return L.Join(if(i=1," ",", ")

Note that UBound is one less than the array length.

\$\endgroup\$
2
  • \$\begingroup\$ This seems to add the comma when there are 2 items - should not \$\endgroup\$
    – edc65
    Commented Sep 18, 2014 at 9:52
  • \$\begingroup\$ Good catch. Edited to fix. \$\endgroup\$
    – silverpie
    Commented Sep 28, 2014 at 3:18
2
\$\begingroup\$

Excel VBA, 108 Bytes

Anonymous VBE immediate window function that takes input as space delimited array from range A1 and outputs to the VBE immediate window.

x=Split([A1]):y=UBound(x):For i=0To y-2:?x(i)", ";:Next:If y>0Then?x(i)IIf(y>1,",","")" and "x(i+1)Else?[A1]
\$\endgroup\$
1
\$\begingroup\$

Racket 87

(define(f x)(string-join(map ~a x)", "#:before-last(if(= 2(length x))" and "", and ")))
\$\endgroup\$
1
  • \$\begingroup\$ I still find the function names in lisps to be way too long for golfing. \$\endgroup\$
    – seequ
    Commented Sep 12, 2014 at 18:29
1
\$\begingroup\$

Python 62 chars

Assuming that i is the list of strings:

(" and", ", and")[len(i) < 2].join(", ".join(i).rsplit(",",1))
\$\endgroup\$
1
\$\begingroup\$

C# 102 Chars

var i=m.Count(),l=i;if(l>2)l--;var r=string.Join(", ",m.Take(l));if(l!=i)r+=" and "+m.Last();return r;
\$\endgroup\$
1
\$\begingroup\$

Julia (53)

print(join(ARGS,", ",(endof(ARGS)>2?",":"")" and "))

Takes args from STDIN and outputs to STDOUT

Solution using Base.join(items, delim[, last])

Edit:

Test cases

julia oxfordComma.jl 1

1

julia oxfordComma.jl 1 2

1 and 2

julia oxfordComma.jl 1 2 3

1, 2, and 3

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I don't know Julia, but this looks like it doesn't generate the Oxford comma. \$\endgroup\$
    – flornquake
    Commented Sep 18, 2014 at 9:32
  • \$\begingroup\$ @flornquake it does print with the Oxford comma, due to the optional "last" argument \$\endgroup\$
    – Cruor
    Commented Sep 18, 2014 at 16:49
  • 2
    \$\begingroup\$ It should be 1, 2, and 3, not 1, 2 and 3. \$\endgroup\$
    – flornquake
    Commented Sep 18, 2014 at 21:11
1
\$\begingroup\$

Rant (108)

[$[l:@b]:[r:each][s:[cmp:[rc];2;[is:different;,]]\s][before:[last:[notfirst:and\s]]][sync:;ordered][arg:b]]

Ungolfed:

[$[l:@b]:
    [r:each]                            # Repeat for each item
    [s:[cmp:[rc];2;[is:different;,]]\s] # Separate by comma if n > 2
    [before:[last:[notfirst:and\s]]]    # Insert "and" before last item
    [sync:;ordered]                     # Force forward order
    [arg:b]                             # Read list
]

Usage:

[$l:{A|B|C|D|E}]

Try it online

\$\endgroup\$
1
\$\begingroup\$

Pyth, 28

j>", "qlQ2+_t_Q+?"and "tQkeQ

Examples:

$ pyth programs/oxford.pyth <<< "['1']"
1

$ pyth programs/oxford.pyth <<< "['1','2']"
1 and 2

$ pyth programs/oxford.pyth <<< "['1','2','3']"
1, 2, and 3
\$\endgroup\$
4
  • \$\begingroup\$ I can't seem to figure out how the input should be formatted. \$\endgroup\$
    – Dennis
    Commented Oct 21, 2014 at 13:55
  • \$\begingroup\$ @Dennis I'll add some test cases. \$\endgroup\$
    – isaacg
    Commented Oct 21, 2014 at 15:33
  • \$\begingroup\$ I had actually tried that, but it didn't work with my version of Pyth (TypeError: can only concatenate list (not "str") to list). It works fine with the latest version. \$\endgroup\$
    – Dennis
    Commented Oct 21, 2014 at 16:58
  • \$\begingroup\$ @Dennis Yeah, the adding to list rule was added quite recently. \$\endgroup\$
    – isaacg
    Commented Oct 21, 2014 at 19:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.