orome
orome

Reputation: 48466

How do I get a single string with an Oxford comma in Groovy?

I'm new to Groovy and am trying to sort out some of the idioms that make it attractive. For example, I'd like to take a list of strings and return a single properly punctuated string built from the list, with each element quoted.

Roughly, the literal (rather generic, and probably brain-dead) approach would be

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
}

Is there a more Groovy way to do this that takes advantage of the language's idioms?

Upvotes: 2

Views: 259

Answers (4)

Michael Easter
Michael Easter

Reputation: 24468

(This question is quite subjective for Stack O.)

My answer uses the meta-class functionality (and other minor features). The solution is not brief in implementation, but is brief in usage (and idiomatic, in the sense of writing a framework):

a=['oxford','cambridge']
println a.toString()

*prints* >> 'oxford' and 'cambridge'

Solution:

// quote
def q = {"'$it'"}

// apply Oxford for N > 2
def f = {it[0..-2].join(", ") + ", and " + it[-1]}

// map list size to Oxford function (N > 2 is default)
def fmap = [:].withDefault{f}
fmap[0] = {""}
fmap[1] = {"${it[0]}"}
fmap[2] = {"${it[0]} and ${it[1]}"}

// re-define toString() to call Oxford function from map
// (with quoted items)
ArrayList.metaClass.toString = { ->
fmap[ delegate.size() ].call( delegate.collect{q(it)} )
}

Example usage:

a=[] ; println a.toString()
a=['oxford'] ; println a.toString()
a=['oxford','cambridge'] ; println a.toString()
a=['oxford','cambridge','comma','space'] ; println a.toString()

Output from examples (_ denotes empty string):

_
'oxford'
'oxford' and 'cambridge'
'oxford', 'cambridge', 'comma', and 'space'

Upvotes: 0

Paweł Piecyk
Paweł Piecyk

Reputation: 2789

Take a look at my solution below. It seems to be complicated at first sight, but it's quite simple. Feel free to ask if you've got any questions.

def oxfordComma(list) {
    list = list.collect { "'$it'" }
    list.size in [1,2] ? list.join(' and ') : list[0..-2].join(', ') + ', and ' + list[-1]
}

assert oxfordComma(['one']) == "'one'"
assert oxfordComma(['one', 'two']) == "'one' and 'two'"
assert oxfordComma(['one', 'two', 'three']) == "'one', 'two', and 'three'"

And shorter version for code golf below ;-)

oxfordComma = { list ->
    list.collect{"'$it'"}.with { it.size in [1,2] ? it.join(' and ') : it[0..-2].join(', ') + ', and ' + it[-1] }
}

Upvotes: 1

Will
Will

Reputation: 14529

A little too long, but hey:

oxford = { list ->
  list.collect { "'$it'" }.with { 
    size() > 1 ? 
      (take(size() - 1) << "and " + last()).join(size() == 2 ? " " : ", ") : it[0]
  }
}

assert oxford(["a", "b"]) == "'a' and 'b'"
assert oxford(["a"]) == "'a'"
assert oxford(["a", "b", "c"]) == "'a', 'b', and 'c'"

Feels like code golf :-)

Upvotes: 1

cfrick
cfrick

Reputation: 37008

take is fine, but a range is shorter. using the same code for the switch case 1 and case 2 turns the switch into an if. with saves you the temp-var.

    def oxford = {
            it.collect({"'$it'"}).with{
                    it.size() < 3 ? it.join(' and ') : "${it[0..-2].join(', ')}, and ${it[-1]}"
            }
    }

    assert oxford(['Larry']) == '\'Larry\''
    assert oxford(['Larry', 'Jeff']) == '\'Larry\' and \'Jeff\''
    assert oxford(['Larry', 'Jeff', 'Leon']) == '\'Larry\', \'Jeff\', and \'Leon\''

Upvotes: 1

Related Questions