Search

February 27, 2020

Groovy Goodness: The Elvis Assignment Operator

Groovy 3 adds a new operator to the language: elvis assignment operator (?=). This operator is a shorthand for an assignment where we want to assign a value to a variable or property if the variable or property is null or false (following Groovy truth). If the value of the variable or property is not null or false (again apply Groovy truth rules), the value stays the same.

In the following example code we use the elvis assignment operator:

// First an example using the elvis operator.
a = null
a = a ?: 100

assert a == 100

// Now we can shorten the assignment using the 
// elvis assignment operator.
a = null
a ?= 100

assert a == 100

b = 200
b ?= 100

assert b == 200

// The elvis assignment operator can also 
// be useful in maps to set default values.
def m = [a: 1, b: 2]
m.a = 100
m.b ?= 200
m.c ?= 300

assert m.a == 100
assert m.b == 2
assert m.c == 300

// Also to set default values for properties
// in an object. Especially if we cannot
// change the class to set a default value.
// In our example we have the source of the
// Email class and we could set default values
// in the class definition, but if the class
// is not under our control we can use the
// elvis assignment operator to set default
// values easily.
class Email {
    String to, from, subject, body
}

def mail = new Email(to: 'test@mrhaki.com', 
                     subject: 'Sample', 
                     body: 'Trying Elvis assignment.')
mail.subject ?= 'Mail has no subject'
mail.from ?= 'no-reply@mrhaki.com'

assert mail.to == 'test@mrhaki.com'
assert mail.from == 'no-reply@mrhaki.com'
assert mail.subject == 'Sample'
assert mail.body == 'Trying Elvis assignment.'

Written with Groovy 3.0.1.

February 23, 2020

Groovy Goodness: Lambda Default Parameter Value

Groovy 3 adds support for Java's lambda syntax expressions. This way we can write code in Groovy using lambda expressions just like in Java. But Groovy adds an additional feature and that is default parameter values for lambda expressions.

In the following example we use a default parameter value for a lambda expression.

// Groovy 3 supports Java's lambda syntax expressions.
def inc = n -> n + 1

assert inc(1) == 2


// But also adds default parameter values.
def multiplyBy = (n, factor = 2) -> n * factor

assert multiplyBy(1) == 2
assert multiplyBy(1, 10) == 10

Written with Groovy 3.0.1.

February 21, 2020

Groovy Goodness: Check Item Is Not In A Collection With !in

Groovy contains lots of little gems that just make live easier and our code more expressive. Groovy 3 doesn't disappoint by adding some nice syntax additions. For example we can now use !in to check if an item is not in a collection, opposed to in that checks if an item is in a collection.

In the following example we use !in:

def list = ['Groovy', 3, 'is', 'awesome', '!']

// New Groovy 3 syntax to check item 
// is NOT in a collection.
assert 'Java' !in list

// Old syntax.
assert !('Java' in list)

// Just to show in still works.
assert 'Groovy' in list

Written with Groovy 3.0.1.

February 19, 2020

Groovy Goodness: Shuffle List or Array

In Java we can use Collections.shuffle method to randomly reorder items in a list. Groovy 3.0.0 adds the shuffle and shuffled methods to a List or array directly. The implementation delegates to Collections.shuffle. The shuffle method will reorder the original list, so there is a side effect using this method. Or we can use the shuffled method that will return a copy of the original list where the items are randomly ordered.

In the next example we use both methods to randomly order lists:

import java.nio.charset.Charset

def signs = (2..10) + ['J', 'Q', 'K', 'A']
def symbols = ['♣', '♦', '♥', '♠']

// Create list as [♣2, ♦2, ♥2, ♠2, ..., ♣A, ♦A, ♥A, ♠A] 
def cards = [symbols, signs].combinations().collect { it.join() }

// Store original cards list.
def deck = cards.asImmutable()

// We should have 52 cards.
assert cards.size() == 52

// Let's shuffle the cards.
// Notice this will change the cards list.
cards.shuffle()
 
assert cards.every { card -> deck.contains(card) }

println cards.take(5) // Possible output: [♣6, ♠A,  ♥Q, ♦Q, ♠5]

// We can use our own Random object for shuffling.
cards.shuffle(new Random(42))

assert cards.every { card -> deck.contains(card) }

println cards.take(5)  // Possible output: [♦5, ♦2, ♦3, ♣7, ♦J]


// Store first 5 cards.
def hand = cards.take(5)

// Using shuffled we get a new list 
// with items in random order. 
// The original list is not changed.
def shuffledCards = cards.shuffled()

assert shuffledCards.size() == cards.size()
assert shuffledCards.every { card -> cards.contains(card) }

// Original list has not changed.
assert hand == cards.take(5)

println shuffledCards.take(5) // Possible output: [♣4, ♠2, ♠6, ♥Q, ♦4]

// We can pass our own Random object.
def randomizer = new Random(42)
def randomCards = cards.shuffled(randomizer)

assert randomCards.size() == cards.size()
assert randomCards.every { card -> cards.contains(card) }

println randomCards.take(5) // Possible output: [♥5, ♠6, ♠8, ♣3, ♠4]

Written with Groovy 3.0.0.

February 17, 2020

Groovy Goodness: Parse YAML With YamlSlurper

In Groovy we have useful classes to parse JSON and XML: JsonSlurper and XmlSlurper. Groovy 3 adds the YamlSlurper class to read in YAML formatted strings. The result of parsing the YAML content is a Map object.

In the next example we have a sample YAML as string that we parse using the parseText method of YamlSlurper:

import groovy.yaml.YamlSlurper

def configYaml = '''\
---
application: "Sample App"
users:
- name: "mrhaki"
  likes: 
  - Groovy
  - Clojure
  - Java
- name: "Hubert"
  likes:
  - Apples
  - Bananas
connections: 
- "WS1"
- "WS2"
'''

// Parse the YAML.
def config = new YamlSlurper().parseText(configYaml)

assert config.application == 'Sample App'

assert config.users.size() == 2
assert config.users[0] == [name: 'mrhaki', likes: ['Groovy', 'Clojure', 'Java']]
assert config.users[1] == [name: 'Hubert', likes: ['Apples', 'Bananas']]

assert config.connections == ['WS1', 'WS2']

We can also use Reader with the parse method of YamlSlurlper:

// Create YAML file.
def yamlFile = new File("sample.yml")
// with YAML contents.
yamlFile.write('''\
---
sample: true
Groovy: "Rocks!"
''')

// Using File.withReader, 
// so reader is closed by Groovy automatically.
yamlFile.withReader { reader ->
    // Use parse method of YamlSlurper.
    def yaml = new YamlSlurper().parse(reader)
    
    assert yaml.sample
    assert yaml.Groovy == 'Rocks!'
}

Finally we need to do an extra step if we want to read in a multiple YAML documents defined in one string or file. The underlying parser of YamlSlurper only reads in one document. A simple workaround is to remove the document separator (---) before parsing the YAML:

def multiDocYaml = '''\
---
version: 1
--- 
loadAtStartup: true
'''

// For YAML with multiple documents separated by ---
// we first need to remove the separators, otherwise
// only the first document is parsed.
def multiDoc = new YamlSlurper().parseText(multiDocYaml.replaceAll('---', ''))

assert multiDoc.version == 1
assert multiDoc.loadAtStartup

Written with Groovy 3.0.0.

February 14, 2020

Groovy Goodness: Create YAML With YamlBuilder

Groovy 3 adds the YamlBuilder class to create YAML output using a Groovy syntax. The YamlBuilder is closely related to JsonBuilder that is described in a previous post. We define a hierarchy using a builder syntax where we can use primitive types, strings, collections and objects. Once we have build our structure we can use the toString() method to get a string representation in YAML format.

In the following example we use YamlBuilder to create YAML:

import groovy.yaml.YamlBuilder

// Sample class and object to transform in YAML.
class User { String firstName, lastName, alias, website }
def userObj = new User(firstName: 'Hubert', lastName: 'Klein Ikkink', alias: 'mrhaki', website: 'https://www.mrhaki.com/') 


// Create YamlBuilder.
def config = new YamlBuilder()

config {
    application 'Sample App'
    version '1.0.1'
    autoStart true
    
    // We can nest YAML content.
    database {
        url 'jdbc:db//localhost'    
    }
    
    // We can use varargs arguments that will
    // turn into a list.
    // We could also use a Collection argument.
    services 'ws1', 'ws2'

    // We can even apply a closure to each
    // collection element.
    environments(['dev', 'acc']) { env ->
        name env.toUpperCase()
        active true
    }

    // Objects with their properties can be converted.
    user(userObj)
}

assert config.toString() == '''\
---
application: "Sample App"
version: "1.0.1"
autoStart: true
database:
  url: "jdbc:db//localhost"
services:
- "ws1"
- "ws2"
environments:
- name: "DEV"
  active: true
- name: "ACC"
  active: true
user:
  firstName: "Hubert"
  alias: "mrhaki"
  lastName: "Klein Ikkink"
  website: "https://www.mrhaki.com/"
'''

Written with Groovy 3.0.0.

February 12, 2020

Groovy Goodness: Calculate Average For Collection

Groovy 3 adds the average method to collections to calculate the average of the items in the collections. When the items are numbers simply the average is calculated. But we can also use a closure as argument to transform an item into a number value and then the average on that number value is calculated.

In the following example code we use the average method on a list of numbers and strings. And we use a closure to first transform an element before calculating the average:

def numbers = [10, 20, 30, 40, 50]

assert numbers.average() == 30

// We can use a closure to transform an item
// and the result is used for calculating an average.
assert numbers.average { n -> n / 10 } == 3


def words = ['Groovy', 'three', 'is', 'awesome']

// Use supported Java method reference syntax to first 
// get length of word.
assert words.average(String::size) == 5

// Calculate average number of vowels in the words.
assert words.average { s -> s.findAll(/a|e|i|o|u/).size() } == 2.25

Written with Groovy 3.0.0.

January 12, 2020

Groovy Goodness: Transform Elements While Flattening

We can use the flatten method in Groovy to flatten a collection that contains other collections into a single collection with all elements. We can pass a closure as extra argument to the flatten method to transform each element that is flattened. The argument of the closure is the element from the original collection.

In the following example we first use the flatten method without a closure argument. Then we pass a closure argument and transform the element:

def list = [1, [2, 3], [[4]]]

// Simple flatten the nested collections.
assert list.flatten() == [1, 2, 3, 4]

// We can use a closure to transform
// the elements in the resulting collection.
assert list.flatten { it * 2 } == [2, 4, 6, 8]

Written with Groovy 2.5.7.

January 10, 2020

Clojure Goodness: Flatten Collections

We can use the flatten function when we have a collection with nested sequential collections as elements and create a new sequence with the elements from all nested collections.

In the following example we use the flatten function:

(ns mrhaki.sample
  (:require [clojure.test :refer [is]]))

;; Elements from nested sequential collections are flattend into new sequence.
(is (= [1 2 3 4 5] (flatten [1 [2 3] [[4]] 5])))
(is (sequential? (flatten [1 [2 3] [[4]] 5])))
(is (= [1 2 3 4 5] (flatten [[1] [2 3] [[4 5]]])))

;; We can use different sequential collection types.
;; We might have to force a type to a sequential collection with seq.
(is (= '(1 2 3 4 5) (flatten [1 (seq (java.util.List/of 2 3)) ['(4 5)]])))
(is (= (quote (1 2 3 4 5)) (flatten [[1] [(range 2 6)]])))

;; flatten on nil returns empty sequence.
(is (= () (flatten nil)))

Written with Clojure 1.10.1.

January 8, 2020

Clojure Goodness: Getting Intersections Between Sets

In the clojure.set namespace we can find the intersection function. This functions accepts one or more sets as arguments and return a new set with all elements that are present in the sets that are passed as arguments to the intersection function. The argument must be a set, so we need to convert other collection or seq values to a set first before we use it as an argument for the function.

In the following example we use one, two or three arguments for the intersection function and also convert other types to a set to be used as argument:

(ns mrhaki.sample
  (:require [clojure.set :refer [intersection]]
            [clojure.test :refer [is]]))

;; Use intersection with sets to find common elements.
(is (= #{"Clojure"} (intersection #{"Java" "Scala" "Clojure"} #{"Clojure" "Groovy"})))

;; An empty set is returned if there is no common element.
(is (= #{} (intersection #{"Java" "Groovy" "Clojure"} #{"C++" "C#"})))

;; We can use more than two sets to find intersections.
(is (= #{"Clojure"} (intersection #{"Java" "Scala" "Clojure"} 
                                  #{"Clojure" "Groovy"} 
                                  #{"Groovy" "JRuby" "Clojure"})))

;; With one set intersections returns the set.
(is (= #{"Clojure" "Groovy"} (intersection #{"Clojure" "Groovy"})))

;; Only sets are allowed as arguments for the intersection function.
;; If one of the arguments is not a set the return value is unexpected. 
(is (= #{} (intersection #{"Clojure" "Groovy"} ["Java" "Scala" "Clojure"])))
;; But we can convert a non-set to a set with the set function.
(is (= #{"Clojure"} (intersection #{"Clojure" "Groovy"} (set ["Java" "Scala" "Clojure"]))))
(is (= #{"Clojure"} (intersection #{"Clojure" "Groovy"} (set '("Java" "Scala" "Clojure")))))
;; Or using into #{}.
(is (= #{"Clojure"} (intersection #{"Clojure" "Groovy"} 
                                  (into #{} (vals {:platform "Java" :language "Clojure"})))))

Written with Clojure 1.10.1