Reputation: 48466
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
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
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
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
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