Power of lambda in Xtend
The Console supports multiple tabs. By hitting CTRL+T combination new tab is opened and auto-named. I decided to name tabs as “Tab 1”, “Tab 2”, etc. However, those tabs can be renamed and moved inbetween so auto-naming need an algorithm. I’ve done similiar operation few times but never done this specific things by using Lambda Expressions in Xtend. I was quite surprised by one small detail.
Required output and algorithm
It’s a very simple need but it makes me remember how specific and and far-sighted you need to be when designing requirements.
Algorithm goes like this:
- find all tabs named as Tab X , where X is a number (multiple digits)
- get the biggest number
- add 1 to the biggest number and use it for new tab name
“The biggest number” is a more wish than well-thought requirement. Between all tab names there could be no single tab named as Tab X . So “the biggest number” requirement could fail when operating on empty collection, e.g. by taking maximum number of collection would throw exception.
Modified algorithm:
- find all tabs named as Tab X , where X is a number (multiple digits)
- sort collected numbers by ascending order
- if there are no numbers then return1 as output, otherwise take max
However, point 3 now is specified as having wrong requirement priority Let’s rewrite it:
- return max, or if there is no number then return 1 as output
The algorithm in Xtend
Let’s start with regular expression that simplifies filtering tabs and taking a number from input:
val numberedTabRegex = Pattern.compile("tab\\s*(\\d+)")
And here’s the algorithm:
val tabNumbers = tabPane.tabs
.map[(it as ConsoleTab).headerText]
.map[numberedTabRegex.matcher(it.toLowerCase)]
.filter[it.find() && it.groupCount == 1]
.map[it.group(1)].map[Integer.parseInt(it)]
.sort
val newTabNumber = if (tabNumbers.empty) 1 else tabNumbers.last + 1
tab.headerText = "Tab " + newTabNumber
While same code would need couple of statements and brackets in Java, here’s almost one single expression. Almost one. I’m a bit judgemental in situations like this but in C# LINQ there’s are functions like FirstOrDefault or LastOrDefault . Xtend doesn’t have it so I needed to write condition for collection emptiness.
In the other hand, it’s quite nice, by comparing to Java 6. Notice keyword it which reduces repeating variable names in situations like here:
val tabNames = tabPane.tabs.map[tab | (tab as ConsoleTab).headerText]
we can have it like this:
val tabNames = tabPane.tabs.map[(it as ConsoleTab).headerText]