Recommended Operators
The prerequisite is that you are familiar with standard assignment and arithmetic operators. If not, read the official Groovy documentation first.
Ternary Operator ? x : y
This operator is used to simplify the following syntax:
if (product.label != null && product.label.length() > 0) { return product.label } else { return 'N/A' }
You can rewrite the above statement using the ternary operator:
return (product.label != null && product.label.length() > 0) ? product.label : 'N/A'
And then simplify it even more since not-empty check in Groovy can be done like this:
return product.label ? product.label : 'N/A'
Elvis Operator ?:
This operator is used to provide a default fallback value if certain variable is empty or null. If the expression in the middle of the ternary operator is identical to its first expression, you should simplify the return statement from the previous example above using ?: operator:
return product.label ?: 'N/A' // Elvis operator
This can be useful if you need, for example, to always return a date when target date is not set:
date = api.targetDate() ?: new Date() // if target date is null, return the current date
Or define a default value for a parameter if it is not set:
lookback = api.decimalUserEntry('Look-back period (default: 20)') ?: 20 // if the user doesn't specify the look-back period, it will default to 20
In case you wonder why this is called Elvis operator:
?: → rotate 90° right → →
Safe Navigation Operator ?.
This operator provides a not-null check before accessing a value from object. Whenever you access object's attribute or method you should check for a non-null reference like this:
if (date) { return date.format('yyyy-MM-dd') } return null
Instead of the case above, it is recommended to use the ternary operator instead:
return date ? date.format('yyyy-MM-dd') : null
But even better is to use the safe navigation operator:
return date?.format('yyyy-MM-dd')
If the date is null, the whole expression will return null. You can even chain multiple occurrences of safe navigation operator:
return product?.attribute21?.toBigDecimal()
Spread Operator *
Imagine the following api.find with many filters:
api.find('P', Filter.equal("attribute1", "P"), Filter.or(Filter.equal("attribute2", "Red"), Filter.equal("attribute2", "Green"), Filter.equal("attribute2", "Blue")),Filter.equal("attribute3", 123))
It works fine, but it's very difficult to read right? Therefore the following syntax is preferred:
def filter = Filter.and( Filter.equal("attribute1", "P"), Filter.or( Filter.equal("attribute2", "Red"), Filter.equal("attribute2", "Green"), Filter.equal("attribute2", "Blue"), ), Filter.equal("attribute3", 123), ) api.find('P', filter)
It is now much easier to read what the filter is supposed to return and also distinguish the priorities between AND and OR, right?
Sometimes you may find the following syntax that has identical meaning:
def filters = [ Filter.equal("attribute1", "P"), Filter.or( Filter.equal("attribute2", "Red"), Filter.equal("attribute2", "Green"), Filter.equal("attribute2", "Blue"), ), Filter.equal("attribute3", 123), ] // notice the * char. This will unwrap each item of the collection and pass it to the method as a separate parameter api.find('P', *filters)
Note that this syntax is representing AND between all filters - that is the behaviour of api.find().
Common mistake is that you forget the * operator in the above case:
api.find('P', filters)
Groovy compiler will throw a compilation error since no version of api.find() accepts a collection as argument but only a variable number of Filter objects:
No signature of method: net.pricefx.formulaengine.scripting.SandboxAPI.find() is applicable for argument types: (java.lang.String, java.util.ArrayList) values: [P, [`attribute1` = "P", (`attribute2` = "Red" or `attribute2` = "Green" or `attribute2` = "Blue"), ...]] Possible solutions: find(), find(java.lang.String, [Lcom.googlecode.genericdao.search.Filter;), find(java.lang.String, int, [Lcom.googlecode.genericdao.search.Filter;), find(groovy.lang.Closure), find(java.lang.String, int, java.lang.String, [Lcom.googlecode.genericdao.search.Filter;), uuid()
Spaceship Operator <=>
This operator delegates to the compareTo
method of the left-side operand and passes it the right-side operand.
assert (1 <=> 1) == 0 assert (1 <=> 2) == -1 assert (2 <=> 1) == 1 assert ('a' <=> 'z') == -1
It is best when mixed with a sort
method.
def competitors = api.productExtension('Competition') def sortedByName = competitors .sort { a, b -> return a.attribute1 <=> b.attribute1 } // assuming attribute1 is the name of the competitor
Found an issue in documentation? Write to us.