<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6671019398434141469</id><updated>2026-03-23T08:22:28.026+01:00</updated><category term="Groovy"/><category term="Groovy:Goodness"/><category term="Groovy programming"/><category term="Grails"/><category term="Gradle"/><category term="Gradle:Goodness"/><category term="Grails:Goodness"/><category term="GroovyGoodness:Intermediate"/><category term="GroovyGoodness:GDK"/><category term="NetBeans"/><category term="NetBeans:Samples"/><category term="GroovyGoodness:Beginner"/><category term="GroovyGoodness:Collections"/><category term="Asciidoctor"/><category term="Awesome:Asciidoctor"/><category term="Java"/><category term="Asciidoc"/><category term="NetBeans 6.1"/><category term="Ratpack"/><category term="Ratpacked"/><category term="Clojure"/><category term="Clojure:Goodness"/><category term="Clojure 1.10.1"/><category term="Spock"/><category term="IntelliJ IDEA"/><category term="Spocklight"/><category term="Maven"/><category term="DataWeave 2.4"/><category term="DataWeave"/><category term="GroovyGoodness:String"/><category term="NetBeans 6.5"/><category term="Spring"/><category term="DataWeave:Delight"/><category term="Spock:Spocklight"/><category term="GradleGoodness:Tasks"/><category term="GroovyGoodness:AST"/><category term="Maven2"/><category term="Ratpack 1.1.1"/><category term="Cocoon"/><category term="Nushell"/><category term="Nushell:Niceties"/><category term="Gradle 0.9"/><category term="Groovy 1.8"/><category term="GrailsGoodness:CommandLine"/><category term="IntelliJ"/><category term="Spock:Spotlight"/><category term="GroovyGoodness:Syntax"/><category term="Java:Joy"/><category term="GrailsGoodness:Configuration"/><category term="GrailsGoodness:Spring"/><category term="PlantUML"/><category term="ClojureGoodness:Collections"/><category term="Cocoon 2.2"/><category term="Grails 3"/><category term="Groovy 2.5.0"/><category term="PlantUML Pleasantness"/><category term="GroovyGoodness:Closure"/><category term="GroovyGoodness:MOP"/><category term="Spring:Sweets"/><category term="GrailsGoodness:Controllers"/><category term="Spring Boot"/><category term="Micronaut"/><category term="Micronaut:Mastery"/><category term="Ratpacked:Configuration"/><category term="Software:Toolbox"/><category term="AwesomeAsciidoctor:Markup"/><category term="Groovy 2.3"/><category term="Weekly Sightings"/><category term="Gradle 0.8"/><category term="GradleGoodness:Commandline"/><category term="Grails 1.1"/><category term="Grails 1.3.7"/><category term="GrailsGoodness:Testing"/><category term="GroovyGoodness:Operators"/><category term="Asciidoctor 0.1.4"/><category term="GradleGoodness:Java"/><category term="Grails 2.3"/><category term="GrailsGoodness:REST"/><category term="PlantUML:Pleasantness"/><category term="Ratpack 1.4.5"/><category term="Spock 1.0"/><category term="DataWeaveDelight:Arrays"/><category term="Gr8Conf"/><category term="Groovy 1.7.3"/><category term="Groovy 3"/><category term="Groovy 5"/><category term="Groovy 5.0.0"/><category term="GroovyGoodness:Date"/><category term="GroovyGoodness:XML"/><category term="AssertJ"/><category term="Awesome:AssertJ"/><category term="ClojureGoodness:Core"/><category term="DataWeaveDelight:Core"/><category term="GroovyGoodness:Advanced"/><category term="GroovyGoodness:DSL"/><category term="IDEA"/><category term="Micronaut 1.0.0.M4"/><category term="PlantUMLPleasantness:Layout"/><category term="Asciidoctor 1.5.0"/><category term="Asciidoctor 1.5.2"/><category term="Asciidoctor 1.5.4"/><category term="GradleGoodness:Plugins"/><category term="Groovy 2.2"/><category term="Helidon"/><category term="Helidon:Helpings"/><category term="HelidonSE:Helpings"/><category term="JavaJoy:Streams"/><category term="Asciidoctor 1.5.1"/><category term="DataWeaveDelight:Strings"/><category term="GradleGoodness:IDE"/><category term="GradleGoodness:Testing"/><category term="Groovy:Goodness GroovyGoodness:GDK"/><category term="Groovy:Grassroots"/><category term="Java 15"/><category term="Kotlin"/><category term="Kotlin:Kandy"/><category term="NetBeans 6.7"/><category term="Ratpack 1.3.3"/><category term="AssertJ 3.24.2"/><category term="Awesome Asciidoctor"/><category term="ClojureGoodness:Functions"/><category term="ClojureGoodness:Sequences"/><category term="Gradle 3.2"/><category term="Grails 1.1.1"/><category term="GrailsGoodness:GSP"/><category term="GroovyGoodness:Annotations"/><category term="Kotlin 1.7.20"/><category term="NetBeans 6.7.1"/><category term="Ratpacked:Handlers"/><category term="Testing"/><category term="Tomcat"/><category term="jq"/><category term="jq 1.7"/><category term="Asciidoctor 1.5.6.1"/><category term="ClojureGoodness:Strings"/><category term="DataWeaveDelight"/><category term="Gradle 1.2"/><category term="Gradle 2.11"/><category term="Grails 2.0"/><category term="Grails 2.2.1"/><category term="Grails 2.2.4"/><category term="Grails 3.0.8"/><category term="Groovy 1.8.1"/><category term="Groovy 2.4.4"/><category term="GroovyGoodness:Files"/><category term="GroovyGoodness:JSON"/><category term="GroovyGoodness:Scripts"/><category term="Helidon 4.1.6"/><category term="IntelliJ IDEA 15"/><category term="MicronautMastery:Controller"/><category term="Spock 0.7"/><category term="SpringSweets:SpringBoot"/><category term="griffon"/><category term="jq:Joy"/><category term="Clojure Goodness"/><category term="ClojureGoodness:Java"/><category term="DataWeave:Core"/><category term="Gr8Conf2012"/><category term="GradleGoodness:BuildScript"/><category term="GradleGoodness:Configuration"/><category term="GradleGoodness:Dependencies"/><category term="GradleGoodness:Model"/><category term="Grails 1.0.x"/><category term="Grails 2.4"/><category term="Grails 3.0.1"/><category term="Groovy 2.4.7"/><category term="Groovy 4.0.3"/><category term="GroovyGoodness:CommandLine"/><category term="GroovyGoodness:SQL"/><category term="Helidon SE"/><category term="IntelliJ IDEA 13"/><category term="JDriven"/><category term="Mastering:Maven"/><category term="Maven:Reference"/><category term="Nushell 0.108.0"/><category term="Ratpack 1.0.0"/><category term="Ratpacked:Registry"/><category term="Ratpacked:Renderer"/><category term="Apache Camel"/><category term="AwesomeAsciidoctor:Structure"/><category term="AwesomeAssertJ:Strings"/><category term="Git"/><category term="Gradle 2.12"/><category term="Gradle 3.1"/><category term="GradleGoodness:Groovy"/><category term="Grails 1.1.2"/><category term="Grails 2.3.7"/><category term="Grails 3.1"/><category term="Grails 3.1.8"/><category term="Groovy 1.7.2"/><category term="Groovy 1.7.4"/><category term="Groovy 2.4"/><category term="Groovy 3.0.1"/><category term="Groovy Goodness Notebook"/><category term="GroovyGoodness:Grape"/><category term="GroovyGoodness:IDE"/><category term="GroovyGoodness:IO"/><category term="HelidonHelpings:Configuration"/><category term="IntelliJ HTTP Client"/><category term="KotlinKandy:Collections"/><category term="KotlinKandy:Strings"/><category term="MasteringMaven:CLI"/><category term="PlantUML 8051"/><category term="PlantUML 8086"/><category term="PlantUMLPleasantness:Commands"/><category term="Ratpacked:HTTP"/><category term="Ratpacked:Promise"/><category term="Ratpacked:Testing"/><category term="SpringSweets"/><category term="XSLT"/><category term="Yahoo Pipes"/><category term="Asciidoctor 2.0.2"/><category term="DataWeave:Arrays"/><category term="GSP"/><category term="Google App Egine"/><category term="Google Guava"/><category term="GoogleGuava:Goodness"/><category term="Gradle 1.0"/><category term="Gradle 2.1"/><category term="Gradle 2.3"/><category term="Gradle 2.7"/><category term="Gradle 7.5.1"/><category term="Gradle 8.6"/><category term="Grails 1.2"/><category term="Grails 2.4.2"/><category term="Grails 3.0.7"/><category term="Grails Goodness Notebook"/><category term="GrailsGoodness:GORM"/><category term="GrailsGoodness:Gradle"/><category term="Groovy 1.7"/><category term="Groovy 2.2.2"/><category term="Groovy 2.3.6"/><category term="Groovy 4.0.11"/><category term="GroovyGoodness:Notebook"/><category term="HTTP Client"/><category term="IntelliJ IDEA 12"/><category term="IntelliJ IDEA 2016.3"/><category term="Java 14"/><category term="Javascript"/><category term="Nushell 0.102.0"/><category term="NushellNiceties:HTTP"/><category term="NushellNiceties:Strings"/><category term="OSX"/><category term="PlantUML 1.2017.16"/><category term="Ratpacked:Groovy"/><category term="Ratpacked:Logging"/><category term="SDKMAN!"/><category term="Spock 2.3"/><category term="Spring Boot 1.5.2.RELEASE"/><category term="SpringSweets:Configuration"/><category term="Wicket"/><category term="jQuery"/><category term="Alfresco"/><category term="Asciidoctor 2.0.9"/><category term="AwesomeAsciidoctor:Tables"/><category term="Clojure 1.11.1"/><category term="ClojureGoodness:Files"/><category term="ClojureGoodness:Sets"/><category term="DataWeave:String"/><category term="DataWeave:Syntax"/><category term="DataWeaveDelight:Objects"/><category term="Flex:Reference"/><category term="Gr8Conf2011"/><category term="Gr8Conf2013"/><category term="Gradle 2.2"/><category term="Gradle 2.2.1"/><category term="Gradle 3.5"/><category term="Gradle 6.8.3"/><category term="Gradle 7.6"/><category term="GradleGoodness:Advanced"/><category term="Grails 3.1.6"/><category term="GrailsGoodness:IDE"/><category term="GrailsGoodness:Logging"/><category term="GrailsGoodness:Notebook"/><category term="Groovy 1.8.3"/><category term="Groovy 2.1"/><category term="Groovy 2.4.6"/><category term="Groovy 2.4.8"/><category term="Groovy 3.0.0"/><category term="Groovy 3.0.2"/><category term="Groovy Server Pages"/><category term="GroovyGoodness:API"/><category term="GroovyGoodness:GINQ"/><category term="GroovyGoodness:Java"/><category term="GroovyGoodness:Testing"/><category term="Helidon 4.1.2"/><category term="HelidonSEHelpings:Observer"/><category term="IntelliJ 2023.2.4"/><category term="IntelliJ IDEA 14 CE"/><category term="IntelliJ IDEA 2016.1"/><category term="Leanpub"/><category term="Log4j"/><category term="Logback"/><category term="Mac"/><category term="Maven 3.9.1"/><category term="Micronaut 1.0.0.RC1"/><category term="MicronautMastery:Client"/><category term="MicronautMastery:Management"/><category term="Mockito"/><category term="MySQL"/><category term="Nushell 0.103.0"/><category term="Nushell 0.104.0"/><category term="Nushell 0.109.1"/><category term="NushellNiceties:Math"/><category term="PlantUMLPleasantness:Skin"/><category term="RESTful webservices"/><category term="Ratpacked:Requests"/><category term="SOAP web services"/><category term="STS"/><category term="Spock 1.1"/><category term="Spock 1.1-groovy-2.4"/><category term="Spocklight Notebook"/><category term="Spring Boot 1.2.5"/><category term="SpringSweets:Actuator"/><category term="UI:Reference"/><category term="sdkman"/><category term="Alfresco 3.4.d"/><category term="Alfresco Web Quick Start"/><category term="Asciidoctor 1.5.5"/><category term="AssertJ 3.26.3"/><category term="AwesomeAsciidoctor:Attributes"/><category term="AwesomeAsciidoctor:Extensions"/><category term="AwesomeAsciidoctor:Macros"/><category term="AwesomeAsciidoctor:Notebook"/><category term="AwesomeAsciidoctor:Source"/><category term="Book Review"/><category term="CSS"/><category term="Clojure 1.11.3"/><category term="ClojureGoodness:Maps"/><category term="ClojureGoodness:Testing"/><category term="CodeNarc"/><category term="Conference"/><category term="DSL"/><category term="DataWeave:Objects"/><category term="Docker"/><category term="Eclipse"/><category term="Flex:Samples"/><category term="GPars"/><category term="Gaelyk"/><category term="Gradle 0.9.1"/><category term="Gradle 1.6"/><category term="Gradle 2.0"/><category term="Gradle 2.13"/><category term="Gradle 3"/><category term="Gradle 3.0"/><category term="Gradle 4.10.2"/><category term="Gradle 5.4"/><category term="Gradle 6.8.2"/><category term="Gradle 8.5"/><category term="Gradle Goodness Notebook"/><category term="GradleGoodness:IntelliJ IDEA"/><category term="GradleGoodness:Kotlin"/><category term="GradleGoodness:Notebook"/><category term="Grails 2.3.5"/><category term="Grails 2.5.0"/><category term="Grails 3.2.6"/><category term="Grails3"/><category term="GrailsGoodness: Spring"/><category term="GrailsGoodness:Actuator"/><category term="GrailsGoodness:Domain"/><category term="GrailsGoodness:Plugins"/><category term="GrailsGoodness:Request"/><category term="GrailsGoodness:URLMapping"/><category term="Griffon:Goodness"/><category term="GriffonGoodness:Beginner"/><category term="Groovy 1.8.6"/><category term="Groovy 2"/><category term="Groovy 2.0"/><category term="Groovy 2.0.4"/><category term="Groovy 2.1.3"/><category term="Groovy 2.2.1"/><category term="Groovy 2.3.1"/><category term="Groovy 2.3.7"/><category term="Groovy 2.4.1"/><category term="Groovy 2.4.10"/><category term="Groovy 2.4.3"/><category term="Groovy 2.4.5"/><category term="Groovy 4"/><category term="Groovy:Goodness GroovyGoodness:XML"/><category term="GroovyGoodness:Annotation"/><category term="GroovyGoodness:Macros"/><category term="GroovyGoodness:TOML"/><category term="GroovyGoodness:YAML"/><category term="Hamcrest"/><category term="HelidonHelpings:Health"/><category term="HelidonSEHelpings:Health"/><category term="IDE"/><category term="IntelliJ 2023.3.2"/><category term="IntelliJ 2023.3.3"/><category term="IntelliJ IDEA 14"/><category term="JSON"/><category term="Java 12"/><category term="Java 16"/><category term="Java 21"/><category term="Java 9"/><category term="JavaJoy:Functions"/><category term="JavaJoy:Optional"/><category term="JavaJoy:Predicate"/><category term="JavaJoy:Regex"/><category term="JavaJoy:String"/><category term="JavaJoy:Strings"/><category term="Keycloak"/><category term="Logging"/><category term="Micronaut 1.0.4"/><category term="MicronautMastery:Configuration"/><category term="MicronautMastery:Reactive"/><category term="Nushell 0.110.0"/><category term="Nushell 0.111.0"/><category term="NushellNiceties:Filters"/><category term="NushellNiceties:Misc"/><category term="PlantUML 1.2018.6"/><category term="PlantUML 8048"/><category term="PlantUMLPleasantness:Command line"/><category term="Ratpacked Notebook"/><category term="Ratpacked:DSL"/><category term="Ratpacked:HttpClient"/><category term="Ratpacked:Paths"/><category term="Ratpacked:SQL"/><category term="Ratpacked:Spring"/><category term="SLF4J"/><category term="Sass"/><category term="Sass 3.2.5"/><category term="Spocklight:Asynchronous"/><category term="Spocklight:Extensions"/><category term="Spocklight:Utilities"/><category term="Spring Boot 1.4.2"/><category term="Spring Boot 2.0.4.RELEASE"/><category term="Spring Boot 3.3.4"/><category term="Spring Boot:Sweets"/><category term="Spring Cloud Contract"/><category term="Spring Sweets"/><category term="SpringBoot"/><category term="SpringSweet:SpringBoot"/><category term="SpringSweets:Gradle"/><category term="XML"/><category term="YUI"/><category term="macOS"/><category term="3"/><category term="Alfresco Web Editor"/><category term="Android"/><category term="Asciidoctor 1.5.7.1"/><category term="Asciidoctor Notebook"/><category term="AsiidoctorJ"/><category term="AssertJ  3.27.6"/><category term="AwesomeAsciidoctor:GitHub"/><category term="AwesomeAsciidoctor:Tools"/><category term="AwesomeAssertJ:Objects"/><category term="AwesomeAssertJ:Optional"/><category term="AwesomeAssertJ:URL"/><category term="Bintray"/><category term="CSS:Reference"/><category term="CXF"/><category term="ClojurGoodness:Core"/><category term="Clojure 1.10.3"/><category term="Clojure 1.11.2"/><category term="ClojureGoodness"/><category term="ClojureGoodness:IO"/><category term="ClojureGoodness:Macros"/><category term="CloudFoundry"/><category term="CloudFoundry:Coolness"/><category term="CodeNarc 0.11"/><category term="Conference report"/><category term="DataWeave:Strings"/><category term="DataWeave:URL"/><category term="Database"/><category term="Firefox"/><category term="Fitnesse"/><category term="GMetrics"/><category term="GMetrics 0.3"/><category term="GlojureGoodness:Sets"/><category term="GoogleGuavaGoodness:Collections"/><category term="Gradle 1.0-rc-3"/><category term="Gradle 2.10"/><category term="Gradle 2.6"/><category term="Gradle 2.8"/><category term="Gradle 3.2.1"/><category term="Gradle 3.3"/><category term="Gradle 3.4"/><category term="Gradle 3.4.1"/><category term="Gradle 4.7"/><category term="Gradle 4.8"/><category term="Gradle 5.1.1"/><category term="Gradle 5.6.2"/><category term="Gradle 6.6.1"/><category term="Gradle 8.0.2"/><category term="GradleGoodness:Ant"/><category term="GradleGoodness:BuildCache"/><category term="GradleGoodness:Cache"/><category term="GradleGoodness:Enterprise"/><category term="GradleGoodness:Files"/><category term="GradleGoodness:Spock"/><category term="GradleGoodness:Task"/><category term="GradleGoodness:VersionCatalog"/><category term="Grafdle 3.2"/><category term="Grails 2"/><category term="Grails 2.0.4"/><category term="Grails 2.2.0"/><category term="Grails 2.2.2"/><category term="Grails 2.3.8"/><category term="Grails 2.4.4"/><category term="Grails 3.0.10"/><category term="Grails 3.0.11"/><category term="Grails 3.0.12"/><category term="Grails 3.0.9"/><category term="Grails 3.2.1"/><category term="Grails 3.2.3"/><category term="Grails CLI"/><category term="Grails:Goodness:Gradle"/><category term="Grails:Plugin"/><category term="GrailsGoodness:Bootstrap"/><category term="GrailsGoodness:DataBinding"/><category term="GrailsGoodness:Database"/><category term="GrailsGoodness:Intermediate"/><category term="GrailsGoodness:JSONViews"/><category term="GrailsGoodness:Marshalling"/><category term="GrailsGoodness:Prompt"/><category term="GrailsGoodness:SQL"/><category term="GrailsGoodness:Scripting"/><category term="GrailsGoodness:Security"/><category term="GrailsGoodness:Taglib"/><category term="GrailsGoodness:Tomcat"/><category term="GrailsGoodness:Validation"/><category term="GrailsGoodness:Web"/><category term="Greach"/><category term="Greachconf"/><category term="GriffonGoodness:CommandLine"/><category term="Groovy 1.6.8"/><category term="Groovy 1.7.9"/><category term="Groovy 1.8.4"/><category term="Groovy 1.8.7"/><category term="Groovy 2.0.5"/><category term="Groovy 2.4.12"/><category term="Groovy 2.5.4"/><category term="Groovy 2.5.7"/><category term="Groovy 4..0.3"/><category term="Groovy 4.0.13"/><category term="Groovy Server Page"/><category term="Groovy:Goodness:GDK"/><category term="GroovyGoodness:"/><category term="GroovyGoodness:Builders"/><category term="GroovyGoodness:CLI"/><category term="GroovyGoodness:CodeNarc"/><category term="GroovyGoodness:Documentation"/><category term="GroovyGoodness:Encoding"/><category term="GroovyGoodness:Functional"/><category term="GroovyGoodness:GKD"/><category term="GroovyGoodness:GPars"/><category term="GroovyGoodness:JMX"/><category term="GroovyGoodness:Java8"/><category term="GroovyGoodness:Lambda"/><category term="GroovyGoodness:Logging"/><category term="GroovyGoodness:RegularExpresssions"/><category term="GroovyGoodness:SDK"/><category term="GroovyGoodness:Streams"/><category term="GroovyGoodness:Templating"/><category term="GroovyGoodness:Threads"/><category term="GroovyGoodness:Traits"/><category term="Guava"/><category term="HTML"/><category term="HelidonHelpings:Observe"/><category term="HelidonHelpings:Request"/><category term="HelidonHelpings:WebServer"/><category term="Hudson"/><category term="IntelliJ 2023.3.4"/><category term="IntelliJ IDEA 2017.2.5"/><category term="IntelliJ IDEA CE"/><category term="Intellij 2022.2.3"/><category term="Iterables"/><category term="JCenter"/><category term="JOOQ"/><category term="Java 13"/><category term="Java 20"/><category term="Java:Lambdas"/><category term="JavaJoy:CLI"/><category term="JavaJoy:Collections"/><category term="JavaJoy:Formatting"/><category term="JavaJoy:Language"/><category term="JavaJoy:RegularExpression"/><category term="Javaland"/><category term="Javaland 2016"/><category term="Javascript:Reference"/><category term="Jenkins"/><category term="JetBrains"/><category term="Jib"/><category term="Jib 0.9.10"/><category term="Keycloak 11.0.2"/><category term="Keycloak 18.0.2"/><category term="Keycloak:Logging"/><category term="Kotlin 1.7.21"/><category term="KotlinKandy:Maps"/><category term="Linux"/><category term="Madrid"/><category term="Mastering Mockito"/><category term="Mastering:Mockito"/><category term="MasteringMaven:AntRun"/><category term="Maven 3.6.3"/><category term="Maven 3.8.6"/><category term="Maven 3.9.9"/><category term="Mercurial"/><category term="MicronautMaster:Testing"/><category term="MicronautMaster:XML"/><category term="MicronautMastery:Beans"/><category term="MicronautMastery:CLI"/><category term="MicronautMastery:JSON"/><category term="MicronautMastery:Reactor"/><category term="MicronautMastery:SSE"/><category term="MicronautMastery:Testing"/><category term="Mockito 3.12.4"/><category term="MuleSoft"/><category term="NextBuild"/><category term="Nushell 0.105.1"/><category term="Nushell 0.107.0"/><category term="Nushell 0.109.0"/><category term="NushellNiceties:Configuration"/><category term="NushellNiceties:Filter"/><category term="NushellNiceties:Lists"/><category term="NushellNiceties:Network"/><category term="NushellNiceties:Operators"/><category term="NushellNiceties:Other"/><category term="NushellNiceties:Random"/><category term="NushellNiceties:StdLib"/><category term="NushellNiceties:URL"/><category term="PlantUML 1.2017.18"/><category term="PlantUML 1.2021.10"/><category term="PlantUMLPleasantness:Fun"/><category term="PlantUMLPleasantness:Icons"/><category term="PlantUMLPleasantness:Variables"/><category term="Ratpack 1.4.0"/><category term="Ratpack 1.4.3"/><category term="Ratpack:Groovy"/><category term="Ratpack:Handlers"/><category term="Ratpack:Paths"/><category term="Ratpacked:Database"/><category term="Ratpacked:Execution Model"/><category term="Ratpacked:Form"/><category term="Ratpacked:IDE"/><category term="Ratpacked:Notebook"/><category term="Ratpacked:Parser"/><category term="Ratpacked:Syntax"/><category term="Ratpacked:Views"/><category term="SDKMAN! 5.15.0"/><category term="SDKMAN! 5.19.0"/><category term="SQL"/><category term="Scala"/><category term="Shell"/><category term="Spain"/><category term="Spock 1.0-SNAPSHOT"/><category term="Spock 1.0-groovy-2.4"/><category term="Spock 1.3-groovy-2.5"/><category term="Spock 2.0"/><category term="Spockframework"/><category term="Spocklight:Dataproviders"/><category term="Spocklight:Date"/><category term="Spring 3"/><category term="Spring 4"/><category term="Spring Boot 1.3.3"/><category term="Spring Boot 1.3.5"/><category term="Spring Boot 2.1.3.RELEASE"/><category term="Spring Boot 2.1.8.RELEASE"/><category term="Spring Boot 3.1.5"/><category term="Spring Boot 3.4.2"/><category term="Spring Cloud"/><category term="Spring Cloud Config"/><category term="Spring Cloud Config Server"/><category term="Spring Cloud Contract 1.0.3.RELEASE"/><category term="SpringSource Tool Suite"/><category term="SpringSweets:Groovy"/><category term="SpringSweets:Logging"/><category term="SpringSweets:Project"/><category term="SpringSweets:Ratpack"/><category term="Subversion"/><category term="Terminal"/><category term="Tomcat6"/><category term="Tomcat7"/><category term="Tools"/><category term="Ubuntu"/><category term="Unicode"/><category term="Video"/><category term="j:Joy"/><category term="plugin"/><category term="sdk"/><category term="starter.spring.io"/><title type='text'>Messages from mrhaki</title><subtitle type='html'>A blog about Groovy, Clojure, Java, Gradle, Asciidoctor and other cool developer subjects.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.mrhaki.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>1528</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-1356332865196951339</id><published>2026-03-20T20:41:00.003+01:00</published><updated>2026-03-20T20:41:25.924+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.111.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Misc"/><title type='text'>Nushell Niceties: Sorting Version Values With Semver Ordering</title><content type='html'>&lt;p&gt;The semver Nushell plugin can be used to work with string values as semver type as you can see in a &lt;a href=&quot;https://blog.mrhaki.com/2026/02/nushell-niceties-transform-values-into.html&quot;&gt;previous post&lt;/a&gt;.
You can use the &lt;code&gt;semver sort&lt;/code&gt; command to sort string values with ordering rules for semantic versions.
With natural ordering of string values a value of &lt;code&gt;10.0.1&lt;/code&gt; is placed before &lt;code&gt;2.1.0&lt;/code&gt;, but if you use &lt;code&gt;semver sort&lt;/code&gt; the ordering will be correct.
The command will look at all the parts of the semver type.
So a major version of &lt;code&gt;2&lt;/code&gt; is placed before &lt;code&gt;10&lt;/code&gt;.
If the major version part is the same than the minor part is used for ordering and so on.
To sort in descending order you can use the option &lt;code&gt;--reverse&lt;/code&gt; or the short option &lt;code&gt;-r&lt;/code&gt;.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example a list of version values is sorted with the &lt;code&gt;semver sort&lt;/code&gt; command:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# After installing the nu_plugin_semver plugin and
# adding it to the plugin registry you can use
# the plugin for Nushell code.
plugin use semver

# `semver sort` uses correct ordering for version values.
(assert equal
        ([&#39;10.0.2&#39; &#39;2.3.1&#39; &#39;1.10.1&#39; &#39;2.3.1-beta.3&#39; &#39;2.3.1-alpha.1&#39;] | semver sort)
        [&#39;1.10.1&#39;
         &#39;2.3.1-alpha.1&#39;
         &#39;2.3.1-beta.3&#39;
         &#39;2.3.1&#39;
         &#39;10.0.2&#39;])

# Without `semver sort` the natural ordering is used, but that is not correct
# for version values. E.g. 10.0.2 is before 2.3.1.
(assert equal
        ([&#39;10.0.2&#39; &#39;2.3.1&#39; &#39;1.10.1&#39; &#39;2.3.1-beta.3&#39; &#39;2.3.1-alpha.1&#39;] | sort)
        [&#39;1.10.1&#39;
         &#39;10.0.2&#39;
         &#39;2.3.1&#39;
         &#39;2.3.1-alpha.1&#39;
         &#39;2.3.1-beta.3&#39;])


# Using the option `--reverse` (shorthand `-r`) to reverse the sorting order.
(assert equal
        ([&#39;10.0.2&#39; &#39;2.3.1&#39; &#39;1.10.1&#39; &#39;2.3.1-beta.3&#39; &#39;2.3.1-alpha.1&#39;] | semver sort --reverse)
        [&#39;10.0.2&#39;
         &#39;2.3.1&#39;
         &#39;2.3.1-beta.3&#39;
         &#39;2.3.1-alpha.1&#39;
         &#39;1.10.1&#39;])&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.111.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/1356332865196951339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/1356332865196951339'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2026/03/nushell-niceties-sorting-version-values.html' title='Nushell Niceties: Sorting Version Values With Semver Ordering'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-1455180616639284530</id><published>2026-03-18T07:26:19.964+01:00</published><updated>2026-03-18T07:27:24.693+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.111.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Other"/><title type='text'>Nushell Niceties: Bumping Semantic Version</title><content type='html'>&lt;p&gt;In a &lt;a href=&quot;https://blog.mrhaki.com/2026/02/nushell-niceties-transform-values-into.html&quot;&gt;previous blogpost&lt;/a&gt; you can learn about the &lt;code&gt;semver&lt;/code&gt; command in Nushell to transform a string value into a semver type.
With the &lt;code&gt;semver bump&lt;/code&gt; command you can increase one of the components of the semver type.
For example to increase the major version part you can use &lt;code&gt;semver bump major&lt;/code&gt;.
This command will also update the minor and patch parts if needed.
The result is a semver type and you can use &lt;code&gt;into value&lt;/code&gt; to transform it to a string type.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example several of the &lt;code&gt;semver bump&lt;/code&gt; commands are used:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# After installing the nu_plugin_semver plugin and
# adding it to the plugin registry you can use
# the plugin for Nushell code.
plugin use semver

# Transform string to semver value.
let version = &#39;4.0.2&#39;

# Bumping major part of version by adding 1 to the current value.
assert equal ($version | semver bump major | into value) &#39;5.0.0&#39;

# Bumping minor part by adding 1 to the value.
# The option `--ignore-errors` makes sure when an error occurs the
# original input value is returned. The shorthand `-i` is also valid.
assert equal ($version | semver bump minor --ignore-errors | into value) &#39;4.1.0&#39;

# Bumping patch part by adding 1 to the value.
assert equal ($version | semver bump patch | into value) &#39;4.0.3&#39;

# Bumping to a release candidate version, patch is unchanged.
assert equal ($version | semver bump rc | into value) &#39;4.0.2-rc.1&#39;

# Bumping to a next alpha version by adding 1 to patch value and
# pre-release part `alpha.1`.
assert equal ($version | semver bump alpha | into value) &#39;4.0.3-alpha.1&#39;

# Bumping to a next beta version by adding 1 to patch value and
# pre-release part `beta.1`.
assert equal ($version | semver bump beta | into value) &#39;4.0.3-beta.1&#39;

# Using `bump release` will remove the pre (release) part of semver value.
assert equal (&#39;4.0.3-rc.1&#39; | semver bump release | into value) &#39;4.0.3&#39;


# The same bump commands can be used for a semver type input.
let full_version = &#39;4.0.2-rc.1+build-20260205&#39; | into semver

assert equal ($full_version | semver bump major | into value) &#39;5.0.0&#39;
assert equal ($full_version | semver bump minor | into value) &#39;4.1.0&#39;
assert equal ($full_version | semver bump patch | into value) &#39;4.0.2+build-20260205&#39;
assert equal ($full_version | semver bump rc | into value) &#39;4.0.2-rc.2+build-20260205&#39;
assert equal ($full_version | semver bump release | into value) &#39;4.0.2+build-20260205&#39;


# The `semver bump` command support the `--build-metadata` (or shorthand `-b`)
# option to add build metadata to a semver or string value.
(assert equal
        (&#39;4.2.8&#39; | semver bump minor --build-metadata &#39;build-20260205&#39; | into value)
        &#39;4.3.0+build-20260205&#39;)&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.111.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/1455180616639284530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/1455180616639284530'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2026/03/nushell-niceties-bumping-semantic.html' title='Nushell Niceties: Bumping Semantic Version'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-4687312169125942384</id><published>2026-02-17T17:27:09.965+01:00</published><updated>2026-02-17T17:29:28.351+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.110.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Misc"/><title type='text'>Nushell Niceties: Transform Values Into Semver Types</title><content type='html'>&lt;p&gt;Nushell can be extended with plugins to have more functionality or types that are not part of standard Nushell.
If you want to work with string values that are actually semantic version values (&lt;a href=&quot;https://semver.org&quot; class=&quot;bare&quot;&gt;https://semver.org&lt;/a&gt;) you can use the &lt;a href=&quot;https://github.com/abusch/nu_plugin_semver&quot;&gt;Nushell SemVer plugin&lt;/a&gt;.
The plugin must be added to Nushell by using the command &lt;code&gt;plugin add &amp;lt;location of plugin&amp;gt;&lt;/code&gt;.
You can check with &lt;code&gt;plugin list&lt;/code&gt; command if the plugin is available.
This command also shows commands that the plugin adds to Nushell.&lt;/p&gt;
&lt;p&gt;The command &lt;code&gt;into semver&lt;/code&gt; can be used to convert string values into a semver type.
The semver type has 5 properties: &lt;code&gt;major&lt;/code&gt;, &lt;code&gt;minor&lt;/code&gt;, &lt;code&gt;patch&lt;/code&gt;, &lt;code&gt;pre&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt;.
You can use dot notation to get the values for these properties or use the &lt;code&gt;get&lt;/code&gt; command.
In the following command a string value is transformed to a semver type: &lt;code&gt;let v = &#39;4.0.2&#39; | into semver&lt;/code&gt;.
And with &lt;code&gt;$v.major&lt;/code&gt; you can extract the major part of the semver value and it returns &lt;code&gt;4&lt;/code&gt;.&lt;br&gt;
Records with the keys &lt;code&gt;major&lt;/code&gt;, &lt;code&gt;minor&lt;/code&gt;, &lt;code&gt;patch&lt;/code&gt;, &lt;code&gt;pre&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; can be transformed to a semver string with the &lt;code&gt;semver from-record&lt;/code&gt; command.
And to transform a semver type to a record you can use the command &lt;code&gt;semver into-record&lt;/code&gt;.&lt;/p&gt;

&lt;a name=&#39;more&#39;&gt;&lt;/a&gt; 

&lt;p&gt;In the following example different string values are transformed and from a semver type or record:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# After installing the nu_plugin_semver plugin and
# adding it to the plugin registry you can use
# the plugin for Nushell code.
plugin use semver

# With `into semver` you can transform a string
# into a semver value with the fields
# major, minor, patch, pre and build.
let version = &#39;4.0.2&#39; | into semver

assert equal $version.major 4
assert equal $version.minor 0
assert equal $version.patch 2
assert equal ($version | get patch) 2

# Type is semver and no longer a string type.
assert equal ($version | describe) semver

# A full version with all parts.
# The pre (release) part is after the patch and - up until the +.
# The build part is everything after the +.
let full_version = &#39;4.0.2-rc.1+build-20260205&#39; | into semver

assert equal $full_version.major 4
assert equal $full_version.minor 0
assert equal $full_version.patch 2
assert equal $full_version.pre &#39;rc.1&#39;
assert equal $full_version.build &#39;build-20260205&#39;

# A version with major, minor, patch and build part.
let build_version = &#39;4.0.2+build-20260205&#39; | into semver

assert equal $build_version.build &#39;build-20260205&#39;
assert equal $build_version.major 4
assert equal $build_version.minor 0
assert equal $build_version.patch 2


# Using `semver to-record` to transform a semver value or string
# to a record with the keys major, minor, patch, pre and build.
assert equal ($version | semver to-record) {
    major: 4 minor: 0 patch: 2 pre: &#39;&#39; build: &#39;&#39;
}
assert equal (&#39;1.2.3&#39; | semver to-record) {
    major: 1 minor: 2 patch: 3 pre: &#39;&#39; build: &#39;&#39;
}
assert equal (&#39;4.0.2-rc.1+build-20260205&#39; | semver to-record) {
    major: 4 minor: 0 patch: 2 pre: &#39;rc.1&#39; build: &#39;build-20260205&#39;
}

# Using `semver from-record` to transform a record with keys
# major, minor, patch, pre and build to a semver value.
(assert equal
        ({major: 4 minor: 0 patch: 2 pre: &#39;&#39; build: &#39;&#39;} | semver from-record)
        &#39;4.0.2&#39;)
(assert equal
        ({major: 4 minor: 0 patch: 2 pre: &#39;rc.1&#39; build: &#39;build-20260205&#39;} | semver from-record)
        &#39;4.0.2-rc.1+build-20260205&#39;)


# A list of string value can be transformed to a list of semver values.
(assert equal ([&#39;4.0.2-rc.1+build-20260205&#39; &#39;1.2.3&#39;] | into semver)
    [(&#39;4.0.2-rc.1+build-20260205&#39; | into semver) (&#39;1.2.3&#39; | into semver)])&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.110.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4687312169125942384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4687312169125942384'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2026/02/nushell-niceties-transform-values-into.html' title='Nushell Niceties: Transform Values Into Semver Types'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-3257386498741540974</id><published>2026-01-28T17:23:00.001+01:00</published><updated>2026-01-28T17:23:44.226+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AssertJ"/><category scheme="http://www.blogger.com/atom/ns#" term="AssertJ  3.27.6"/><category scheme="http://www.blogger.com/atom/ns#" term="Awesome:AssertJ"/><category scheme="http://www.blogger.com/atom/ns#" term="AwesomeAssertJ:Strings"/><title type='text'>Awesome AssertJ: Use isEqualToNormalizingNewlines To Assert With Text Block</title><content type='html'>&lt;p&gt;A Java text block is an easy way to have a multiline string value.
But there is a catch if we want to use a text block with the assertion method &lt;code&gt;isEqualTo&lt;/code&gt;.
Suppose you have written a piece of code that create a new string value where the line endings are defined using &lt;code&gt;System.lineSeparator()&lt;/code&gt;.
The string value would have the line ending &lt;code&gt;\n&lt;/code&gt; on Linux and MacOS systems, and the line ending &lt;code&gt;\r\n&lt;/code&gt; on Windows system.
But a Java text block will &lt;strong&gt;always&lt;/strong&gt; use the line ending &lt;code&gt;\n&lt;/code&gt; on every platform, including the Windows platform.
If you would run your tests on a Windows platform then the assertion using &lt;code&gt;isEqualTo&lt;/code&gt; will fail, but the test will pass on Linux or MacOS systems.
This is a problem if you are working with developers using different operating systems.
Therefore it is better to use the method &lt;code&gt;isEqualToNormalizingNewlines&lt;/code&gt; for these type of assertions.
AssertJ will make sure the line endings are the same and the tests will pass independent of the operating system the tests are run on.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example you can see usage of the &lt;code&gt;isEqualToNormalizingNewlines&lt;/code&gt; method:&lt;/p&gt;

&lt;pre class=&quot;brush:java;&quot;&gt;package mrhaki;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class TextBlock {

  @Test
  void shouldEqualWithNormalizingLines() {
    var text = &quot;Generated text with multiple lines&quot;
            + System.lineSeparator()
            + &quot;separated by the platform specific line ending.&quot;
            + System.lineSeparator()
            + &quot;On Windows \r\n and on Linux and MacOS \n.&quot;;

    var expected =
        &quot;&quot;&quot;
        Generated text with multiple lines
        separated by the platform specific line ending.
        On Windows \r\n and on Linux and MacOS \n.&quot;&quot;&quot;;

    // assertThat(text).isEqualTo(expected); fails when this
    // test is run on a Windows machine, because the line ending
    // in a Java text block is always \n, even on a Windows machine.
    // It is safer to use isEqualToNormalizingNewLines if the assertion
    // is done with a Java text block.
    assertThat(text).isEqualToNormalizingNewlines(expected);
  }
}&lt;/pre&gt;
&lt;p&gt;Written with AssertJ 3.27.6&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/3257386498741540974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/3257386498741540974'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2026/01/awesome-assertj-use-isequaltonormalizin.html' title='Awesome AssertJ: Use isEqualToNormalizingNewlines To Assert With Text Block'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-1249048657769312445</id><published>2026-01-25T10:42:00.002+01:00</published><updated>2026-01-25T10:42:32.537+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.110.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Operators"/><title type='text'>Nushell Niceties: Checking If Value Is In List Or String Or Key In Record</title><content type='html'>&lt;p&gt;Nushell has the &lt;code&gt;in&lt;/code&gt; operator to check if a value is an element of list.
With the same &lt;code&gt;in&lt;/code&gt; operator you can check if a string is a substring of another string.
And finally you can use the &lt;code&gt;in&lt;/code&gt; operator to check if a key is in a record.
When you use the operator the value you want to check is defined before the operator and the list, other string or record is defined after the operator.&lt;br&gt;
The &lt;code&gt;not-in&lt;/code&gt; operator is the opposite of the &lt;code&gt;in&lt;/code&gt; operator and checks if a value is &lt;strong&gt;not&lt;/strong&gt; in list or other string or key in a record.&lt;br&gt;
It is also possible to use the &lt;code&gt;has&lt;/code&gt; and &lt;code&gt;not-has&lt;/code&gt; operators to do the same checks, but the value you want to check is set after the operator.
The list, other string or record is set before the operator.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example the operators are used with lists:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using in operator to check if a value is in a list.
assert (1 in [1 2 3 4])
assert (&#39;Nushell&#39; in [&#39;Nushell&#39; &#39;rocks&#39;])

# Using not-in operator to check if a value is not in a list.
assert (1 not-in [2 3 4])
assert (&#39;nushell&#39; not-in [&#39;Nushell&#39; &#39;rocks&#39;])


# Using has operator to check if a value is in a list.
assert ([1 2 3 4] has 1)
assert ([&#39;Nushell&#39; &#39;rocks&#39;] has &#39;Nushell&#39;)

# Using not-has operator to check if a value is not in a list.
assert ([1 2 3 4] not-has 5)
assert ([&#39;Nushell&#39; &#39;rocks&#39;] not-has &#39;nushell&#39;)&lt;/pre&gt;
&lt;p&gt;In the following example the operators are used with strings:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using in operator to check if a value is in a string.
assert (&#39;Nushell&#39; in &#39;Nushell rocks!&#39;)

# Using not-in operator to check if a value is not in a string.
assert (&#39;awesome&#39; not-in &#39;Nushell rocks!&#39;)


# Using has operator to check if a value is in a string.
assert (&#39;Nushell rocks&#39; has &#39;Nushell&#39;)

# Using not-has operator to check if a value is not in a string.
assert (&#39;Nushell rocks&#39; not-has &#39;awesome&#39;)&lt;/pre&gt;
&lt;p&gt;In the following example the operators are used with records:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using in operator to check if a key is in a record.
assert (&#39;name&#39; in {name: &#39;mrhaki&#39; location: &#39;Tilburg&#39;})

# Using not-in operator to check if a key is not in a record.
assert (&#39;age&#39; not-in {name: &#39;mrhaki&#39; location: &#39;Tilburg&#39;})


# Using has operator to check if a key is in a record.
assert ({name: &#39;mrhaki&#39; location: &#39;Tilburg&#39;} has name)

# Using not-has operator to check if a key is not in a record.
assert ({name: &#39;mrhaki&#39; location: &#39;Tilburg&#39;} not-has age)&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.110.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/1249048657769312445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/1249048657769312445'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2026/01/nushell-niceties-checking-if-value-is.html' title='Nushell Niceties: Checking If Value Is In List Or String Or Key In Record'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-3800377381982058297</id><published>2026-01-08T18:15:00.002+01:00</published><updated>2026-01-08T18:15:45.594+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.109.1"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Math"/><title type='text'>Nushell Niceties: Calculating The Average Of Numeric Values</title><content type='html'>&lt;p&gt;In order to calculate an average for a list of numbers, file sizes, durations, or range of numbers, or a table with columns containing numeric values you can use the &lt;code&gt;math avg&lt;/code&gt; command.
This command is part of the &lt;code&gt;math&lt;/code&gt; module of Nushell.
When the input is a list then the result is a single value with the average of all values in the list.
If you use a table as input the result is a record where the key is the column name and the value the average of all values in that column.
Finally it is possible to have a single value as input and the result is the same value obviously.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;The following example has several uses of the &lt;code&gt;math avg&lt;/code&gt; command:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using math avg on a list of numbers.
assert equal ([1 2 3] | math avg) 2

# math avg also works on a single value
# and returns simply the value.
assert equal (1 | math avg) 1

# Using math avg on a range of numbers.
assert equal (5..10 | math avg) 7.5

# Using math avg on a list of durations.
assert equal ([1min 15sec 1.5min] | math avg) 55sec

# math avg also works on a single value
# and returns simply the value.
assert equal (1min | math avg) 1min

# Using math avg on a list of sizes.
assert equal ([200MB 100MB 0.6GB] | math avg) 300MB

# math avg also works on a single value
# and returns simply the value.
assert equal (100MB | math avg) 100MB

# Helper table with columns name, age, height (in cm).
let table = [
    [name age height];
    [Alice 30 175]
    [Bob 25 185]
    [Charlie 35 180]
]

# Using math avg on a table result in a record with for each
# numeric column the avg of all values.
assert equal ($table | math avg) { age: 30 height: 180 }&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.109.1.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/3800377381982058297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/3800377381982058297'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2026/01/nushell-niceties-calculating-average-of.html' title='Nushell Niceties: Calculating The Average Of Numeric Values'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-6013700203690928020</id><published>2026-01-08T06:50:01.393+01:00</published><updated>2026-01-08T06:50:44.474+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.109.1"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Math"/><title type='text'>Nushell Niceties: Calculating The Sum Of Numeric Values</title><content type='html'>&lt;p&gt;The &lt;code&gt;math&lt;/code&gt; module in Nushell has a lot of useful commands to work with numeric values.
You can use the &lt;code&gt;math sum&lt;/code&gt; command to calculate the sum of a multiple numeric values.
The input of the command can be a list of numbers, durations, file sizes, or a range or table with columns containing numeric values.
The result is a single numeric value with the sum of all values in the input.
The &lt;code&gt;math sum&lt;/code&gt; command can also be used on a table with multiple numeric columns.
It will return a record with the sum of all values for each column.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example you can see several uses of the &lt;code&gt;math sum&lt;/code&gt; command with different input types:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using math sum on a list of numbers.
assert equal ([1 2 3] | math sum) 6

# Using math sum on a range of numbers.
assert equal (5..10 | math sum) 45

# Using math sum on a list of durations.
assert equal ([1min 15sec 1.5min] | math sum) 165sec

# Using math sum on a list of sizes.
assert equal ([200MB 100MB 0.5GB] | math sum) 800MB

# Helper table with columns name, age, height (in cm).
let table = [
    [name age height];
    [Alice 30 175]
    [Bob 25 183]
    [Charlie 35 180]
]

# Using math sum on a table result in a record with for each
# numeric column the sum of all values.
assert equal ($table | math sum) { age: 90 height: 538 }&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.109.1.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6013700203690928020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6013700203690928020'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2026/01/nushell-niceties-calculating-sum-of.html' title='Nushell Niceties: Calculating The Sum Of Numeric Values'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-8781103891758829136</id><published>2026-01-06T17:58:00.002+01:00</published><updated>2026-01-06T17:58:14.148+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.109.1"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Math"/><title type='text'>Nushell Niceties: Getting Minimum And Maximum Values</title><content type='html'>&lt;p&gt;In Nushell we can use a lot a math related commands to get for example the minimum and maximum values of a list of numbers.
In the &lt;code&gt;math&lt;/code&gt; module you can find the commands &lt;code&gt;math min&lt;/code&gt; and &lt;code&gt;math max&lt;/code&gt;.
You can use these commands to get the minimum and maximum values of a list of numbers, durations, file sizes, a range and tables.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example you can see usages of the &lt;code&gt;math min&lt;/code&gt; and &lt;code&gt;math max&lt;/code&gt; commands on different input types:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# math min

# Using math min on a list of numbers.
assert equal ([1 2 3] | math min) 1

# Using math min on a range of numbers.
assert equal (5..10 | math min) 5

# Using math min on a list of durations.
assert equal ([1min 61sec 1.5min] | math min) 1min

# Using math min on a list of sizes.
assert equal ([200MB 100MB 0.5GB] | math min) 100MB

# Using math min on a list of strings.
assert equal ([&quot;Alice&quot; &quot;Bob&quot; &quot;Charlie&quot;] | math min) &quot;Alice&quot;

# Helper table with columns name, age, height (in cm).
let table = [
    [name age height];
    [Alice 30 175]
    [Bob 25 183]
    [Charlie 35 180]
]

# Using math min on a table result in a record with for each column the minimum value.
assert equal ($table | math min) { name: Alice age: 25 height: 175 }

# math max

# Using math max on a list of numbers.
assert equal ([1 2 3] | math max) 3

# Using math max on a range of numbers.
assert equal (5..10 | math max) 10

# Using math max on a list of durations.
assert equal ([1min 61sec 1.5min] | math max) 1.5min

# Using math max on a list of sizes.
assert equal ([200MB 100MB 0.5GB] | math max) 0.5GB

# Using math max on a list of strings.
assert equal ([&quot;Alice&quot; &quot;Bob&quot; &quot;Charlie&quot;] | math max) &quot;Charlie&quot;

# Using math max on a table result in a record with for each column the maximum value.
assert equal ($table | math max) { name: Charlie age: 35 height: 183 }&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.109.1.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/8781103891758829136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/8781103891758829136'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2026/01/nushell-niceties-getting-minimum-and.html' title='Nushell Niceties: Getting Minimum And Maximum Values'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-4872502387000405617</id><published>2025-12-10T07:43:00.002+01:00</published><updated>2025-12-10T07:43:57.280+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.109.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Strings"/><title type='text'>Nushell Niceties: Checking String Starts Or Ends With Given String</title><content type='html'>&lt;p&gt;To check if a string value starts or ends with a given string you can use the &lt;code&gt;str starts-with&lt;/code&gt; and &lt;code&gt;str ends-with&lt;/code&gt; commands.
The command returns a boolean value: &lt;code&gt;true&lt;/code&gt; if the string starts or ends with the given string and &lt;code&gt;false&lt;/code&gt; otherwise.
The commands are case sensitive by default.
You can use the &lt;code&gt;--ignore-case&lt;/code&gt; (or the shorthand &lt;code&gt;-i&lt;/code&gt;) to ignore casing while checking if a the string starts or ends with a given string.&lt;br&gt;
To input can be a string value and then that string value is checked.
If the input is an array of string values, then each element is checked.
It is also possible to check values in a record or table.
You need to pass the names of the field(s) or column(s) that you want to check the string values of.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;The next example shows several uses of the &lt;code&gt;str starts-with&lt;/code&gt; command:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

assert (&quot;mrhaki&quot; | str starts-with &quot;mr&quot;)
assert not (&quot;Hubert&quot; | str starts-with &quot;mr&quot;)

# Using --ignore-case to check with ignoring casing.
assert (&quot;MrHaki&quot; | str starts-with --ignore-case &quot;mr&quot;)

# Using shorthand -i to check with ignoring casing.
assert (&quot;MrHaki&quot; | str starts-with -i &quot;mr&quot;)


# With an array as input each element is tested to
# see if it starts with the given string.
assert equal ([&quot;mrhaki&quot; &quot;Hubert&quot;] | str starts-with &quot;mr&quot;) [true false]


# With a record as input you can give the name of
# the field(s) to check if the value is starting with
# a given string.
let data = {name: &quot;mrhaki&quot;, shell: &quot;NuShell&quot;}
assert ($data | str starts-with &quot;mr&quot; name).name
assert not ($data | str starts-with &quot;mr&quot; shell).shell
(assert equal ($data | str starts-with &quot;mr&quot; name shell)
              {name: true shell: false})


# With a table as input you can give the name of
# the field(s) to check if the value is starting with
# a given string.
let users = [[name shell]; [&quot;mrhaki&quot; &quot;NuShell&quot;] [&quot;Hubert&quot; &quot;bash&quot;]]
assert equal ($users | str starts-with &quot;mr&quot; name).name [true false]
assert equal ($users | str starts-with &quot;mr&quot; shell).shell [false false]
(assert equal ($users | str starts-with &quot;mr&quot; name shell)
              [[name shell]; [true false] [false false]])&lt;/pre&gt;
&lt;p&gt;In the following example you can see several ways to use the &lt;code&gt;str ends-with&lt;/code&gt; command:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

assert (&quot;Nushell is awesome!&quot; | str ends-with &quot;!&quot;)
assert not (&quot;Nushell rocks&quot; | str ends-with &quot;!&quot;)

# Using --ignore-case to check with ignoring casing.
assert (&quot;Nushell rocks&quot; | str ends-with --ignore-case &quot;Rocks&quot;)

# Using shorthand -i to check with ignoring casing.
assert (&quot;Nushell rocks&quot; | str ends-with -i &quot;Rocks&quot;)


# With an array as input each element is tested to
# see if it ends with the given string.
(assert equal ([&quot;Nushell is awesome!&quot; &quot;Nushell rocks&quot;] | str ends-with &quot;!&quot;)
              [true false])


# With a record as input you can give the name of
# the field(s) to check if the value is ending with
# a given string.
let data = {name: &quot;Nushell&quot;, exclamation: &quot;rocks!&quot;}
assert ($data | str ends-with &quot;!&quot; exclamation).exclamation
assert not ($data | str ends-with &quot;!&quot; name).name
(assert equal ($data | str ends-with &quot;!&quot; name exclamation)
              {name: false exclamation: true})


# With a table as input you can give the name of
# the field(s) to check if the value is starting with
# a given string.
let shells = [[name exclamation]; [&quot;Nushell&quot; &quot;rocks!&quot;] [&quot;Nushell&quot; &quot;awesome&quot;]]
assert equal ($shells | str ends-with &quot;!&quot; exclamation).exclamation [true false]
assert equal ($shells | str ends-with &quot;!&quot; name).name [false false]
(assert equal ($shells | str ends-with &quot;!&quot; name exclamation)
              [[name exclamation]; [false true] [false false]])&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.109.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4872502387000405617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4872502387000405617'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/12/nushell-niceties-checking-string-starts.html' title='Nushell Niceties: Checking String Starts Or Ends With Given String'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-7456837052525633862</id><published>2025-12-02T21:19:11.296+01:00</published><updated>2025-12-10T07:44:24.878+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.108.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><title type='text'>Nushell Niceties: Tables With Different Themes</title><content type='html'>&lt;p&gt;A lot of commands have output displayed as table.
It is possible to use different themes for tables.
If you run the command &lt;code&gt;table --list&lt;/code&gt; you get all available themes.
At the moment the following themes are available: basic, compact, compact_double, default, heavy, light, none, reinforced, rounded, thin, with_love, psql, markdown, dots, restructured, ascii_rounded, basic_compact, single, double.
You can use a theme with the &lt;code&gt;table&lt;/code&gt; command by using the &lt;code&gt;--theme&lt;/code&gt; option and the name of the theme.
For example to have a different table theme for the &lt;code&gt;ls&lt;/code&gt; command you can use &lt;code&gt;ls | table --theme light&lt;/code&gt;.&lt;br&gt;
If you want to change the theme for all table output you can set the configuration option &lt;code&gt;$env.config.table.mode&lt;/code&gt;.
To make this configuration setting permanent you can add it to &lt;code&gt;config.nu&lt;/code&gt; file.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following examples all different themes and how they look are shown:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;light:true&quot;&gt;&amp;gt; [[a b]; [1 2] [3 4]] | table --theme basic --index false
+---+---+
| a | b |
+---+---+
| 1 | 2 |
+---+---+
| 3 | 4 |
+---+---+

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme compact --index false
───┬───
 a │ b
───┼───
 1 │ 2
 3 │ 4
───┴───

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme compact_double --index false
═══╦═══
 a ║ b
═══╬═══
 1 ║ 2
 3 ║ 4
═══╩═══

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme default --index false
╭───┬───╮
│ a │ b │
├───┼───┤
│ 1 │ 2 │
│ 3 │ 4 │
╰───┴───╯

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme heavy --index false
┏━━━┳━━━┓
┃ a ┃ b ┃
┣━━━╋━━━┫
┃ 1 ┃ 2 ┃
┃ 3 ┃ 4 ┃
┗━━━┻━━━┛

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme light --index false
 a   b
───────
 1   2
 3   4

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme none --index false
 a   b
 1   2
 3   4

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme reinforced --index false
┏───┬───┓
│ a │ b │
│ 1 │ 2 │
│ 3 │ 4 │
┗───┴───┛

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme rounded --index false
╭───┬───╮
│ a │ b │
├───┼───┤
│ 1 │ 2 │
│ 3 │ 4 │
╰───┴───╯

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme thin --index false
┌───┬───┐
│ a │ b │
├───┼───┤
│ 1 │ 2 │
├───┼───┤
│ 3 │ 4 │
└───┴───┘

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme with_love --index false
❤❤❤❤❤❤❤
 a ❤ b
❤❤❤❤❤❤❤
 1 ❤ 2
 3 ❤ 4
❤❤❤❤❤❤❤

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme psql --index false
 a | b
---+---
 1 | 2
 3 | 4

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme markdown --index false
| a | b |
|---|---|
| 1 | 2 |
| 3 | 4 |

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme dots --index false
.........
: a : b :
: 1 : 2 :
: 3 : 4 :
:...:...:

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme restructured --index false
=== ===
 a   b
=== ===
 1   2
 3   4
=== ===

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme ascii_rounded --index false
.-------.
| a | b |
| 1 | 2 |
| 3 | 4 |
&#39;-------&#39;

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme basic_compact --index false
+---+---+
| a | b |
| 1 | 2 |
| 3 | 4 |
+---+---+

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme single --index false
┌───┬───┐
│ a │ b │
├───┼───┤
│ 1 │ 2 │
│ 3 │ 4 │
└───┴───┘

&amp;gt; [[a b]; [1 2] [3 4]] | table --theme double --index false
╔═══╦═══╗
║ a ║ b ║
╠═══╬═══╣
║ 1 ║ 2 ║
║ 3 ║ 4 ║
╚═══╩═══╝&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.108.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/7456837052525633862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/7456837052525633862'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/12/nushell-niceties-tables-with-different.html' title='Nushell Niceties: Tables With Different Themes'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-7684747896340836020</id><published>2025-11-14T21:42:00.005+01:00</published><updated>2025-11-19T15:09:39.941+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.108.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:StdLib"/><title type='text'>Nushell Niceties: Summon Ellie The Nushell Mascot</title><content type='html'>&lt;p&gt;When you start Nushell you can see a nice ASCII art elephant.
That is Ellie a cute and friendly elephant mascot for Nushell.
Elephants are popular as you can see them in other products like Mastodon and Gradle.
It is possible to summon Ellie in your Nushell environment by running the &lt;code&gt;ellie&lt;/code&gt; command.
This command is part of the &lt;code&gt;std&lt;/code&gt; library and you need to run &lt;code&gt;use std ellie&lt;/code&gt; or &lt;code&gt;std use *&lt;/code&gt; first.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example you can see the output of the &lt;code&gt;ellie&lt;/code&gt; command:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;&amp;gt; use std ellie
&amp;gt; ellie
     __  ,
 .--()°&#39;.&#39;
&#39;|, . ,&#39;
 !_-(_\
&amp;gt;&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.108.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/7684747896340836020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/7684747896340836020'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/11/nushell-niceties-summon-ellie-nushell.html' title='Nushell Niceties: Summon Ellie The Nushell Mascot'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-574239191133333334</id><published>2025-11-06T07:45:09.537+01:00</published><updated>2025-11-10T08:35:29.649+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.108.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Filters"/><title type='text'>Nushell Niceties: Checking For Emptiness</title><content type='html'>&lt;p&gt;To check if a value is empty you can use the &lt;code&gt;is-empty&lt;/code&gt; command.
The command can be used for string values, lists and records.&lt;br&gt;
The opposite check to see if a value is not empty can be done with the &lt;code&gt;is-not-empty&lt;/code&gt; command.
Just like with the &lt;code&gt;is-empty&lt;/code&gt; command this can be used for string values, lists and records.
To check if values in a table column are not empty the names of the columns to check can be passed as argument.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example several uses of &lt;code&gt;is-empty&lt;/code&gt; and &lt;code&gt;is-not-empty&lt;/code&gt; are shown:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Empty string value
assert (&#39;&#39; | is-empty)

# Empty list
assert ([] | is-empty)

# Empty record
assert ({} | is-empty)

# Evaluate is-empty for all rows.
assert ([[name type]; [Nushell &#39;&#39;] [bash &#39;&#39;]] | all { |row| $row.type | is-empty })


# Non-empty string value
assert (&#39;Nushell&#39; | is-not-empty)

# Non-empty list
assert ([Nushell rocks] | is-not-empty)

# Non-empty record
assert ({name: Nushell type: shell} | is-not-empty)

# Evaluate is-no-empty for all rows.
assert ([[name type]; [Nushell shell] [bash posix]] | all { |row| $row.type | is-not-empty })&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.108.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/574239191133333334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/574239191133333334'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/11/nushell-niceties-checking-for-emptiness.html' title='Nushell Niceties: Checking For Emptiness'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-5527022372746102351</id><published>2025-11-01T19:53:00.003+01:00</published><updated>2025-11-01T19:53:19.848+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.108.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Filters"/><title type='text'>Nushell Niceties: Getting Column And Key Names</title><content type='html'>&lt;p&gt;To get the names of columns in a table or the keys of a record you can use the &lt;code&gt;columns&lt;/code&gt; command.
The command returns a list of string values that are the column or key names.
When the input is table the column names are returned, and when the input is a record the names of the keys are returned.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example code the &lt;code&gt;columns&lt;/code&gt; command is used with a record and table:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# columns on a record return the record keys.
let record = {alias: mrhaki name: &quot;Hubert Klein Ikkink&quot; country: &quot;The Netherlands&quot;}
assert equal ($record | columns) [alias name country]

# columns used with a table return the column names.
let table = [
  [name ship age];
  [&quot;Jack Sparrow&quot; &quot;Black Pearl&quot; 40]
  [&quot;Will Turner&quot; &quot;Flying Dutchman&quot; 35]
]
assert equal ($table | columns) [name ship age]&lt;/pre&gt;
&lt;p&gt;Output of commands that return a table sometimes do not fit on the screen.
With the &lt;code&gt;columns&lt;/code&gt; command the column names can be fetched and with &lt;code&gt;select&lt;/code&gt; the number of columns in the output can be less so it fits on the screen.&lt;/p&gt;
&lt;p&gt;In the following examples several uses of &lt;code&gt;columns&lt;/code&gt; are shown:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;# Using columns to get column names for ls command output.
assert equal (ls | columns) [name type size modified]

# Using --long option gets more column names.
# Output can differ per operating system, this is for MacOS.
(assert equal (ls --long | columns)
              [name type target readonly mode
               num_links inode user group
               size created accessed modified])


let url = &quot;https://httpbin.org/json&quot;

# JSON response https://httpbin.org/json
# {
#   &quot;slideshow&quot;: {
#     &quot;author&quot;: &quot;Yours Truly&quot;,
#     &quot;date&quot;: &quot;date of publication&quot;,
#     &quot;slides&quot;: [
#       {
#         &quot;title&quot;: &quot;Wake up to WonderWidgets!&quot;,
#         &quot;type&quot;: &quot;all&quot;
#       },
#       {
#         &quot;items&quot;: [
#           &quot;Why &amp;lt;em&amp;gt;WonderWidgets&amp;lt;/em&amp;gt; are great&quot;,
#           &quot;Who &amp;lt;em&amp;gt;buys&amp;lt;/em&amp;gt; WonderWidgets&quot;
#         ],
#         &quot;title&quot;: &quot;Overview&quot;,
#         &quot;type&quot;: &quot;all&quot;
#       }
#     ],
#     &quot;title&quot;: &quot;Sample Slide Show&quot;
#   }
# }

let url_content = http get $url

assert equal ($url_content | columns) [slideshow]
(assert equal ($url_content | get slideshow | columns)
              [author date slides title])
(assert equal ($url_content | get slideshow | select author title)
              { author: &quot;Yours Truly&quot; title: &quot;Sample Slide Show&quot; })&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.108.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/5527022372746102351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/5527022372746102351'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/11/nushell-niceties-getting-column-and-key.html' title='Nushell Niceties: Getting Column And Key Names'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-1820601623915566132</id><published>2025-11-01T14:17:00.003+01:00</published><updated>2025-11-01T19:53:46.962+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.108.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:HTTP"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:URL"/><title type='text'>Nushell Niceties: Create Query Parameters For URL</title><content type='html'>&lt;p&gt;The build-in HTTP client in Nushell can be used to interact with REST APIs and websites.
If the URL you want to invoke has query parameters than you can use the &lt;code&gt;url build-query&lt;/code&gt; command.
The &lt;code&gt;url build-query&lt;/code&gt; command transforms a record or table to URL encoded key/value pairs joined with an ampersand (&lt;code&gt;&amp;amp;&lt;/code&gt;).
Each key and value is separated by an equal sign (&lt;code&gt;=&lt;/code&gt;).
The command can expand a key with a list value to separate key/value pairs with the same key if the key is defined in a record.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example &lt;code&gt;url build-query&lt;/code&gt; is used to create query parameters for a URL:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using url build-query to convert record to valid
# query parameters that can be used in a URL.
(assert equal ({items: 20, page: 1, order_by: name, direction: &quot;asc&quot;} | url build-query)
			 &quot;items=20&amp;amp;page=1&amp;amp;order_by=name&amp;amp;direction=asc&quot;)

# Values are automatically URL encoded with url build-query.
(assert equal ({name: &#39;Hubert Klein Ikkink&#39;, fav_candy: &#39;M&amp;amp;M&#39;} | url build-query)
             &#39;name=Hubert+Klein+Ikkink&amp;amp;fav_candy=M%26M&#39;)

# Key with a list value is automatically expanded.
(assert equal ({order_by: [name, age]} | url build-query)
              &quot;order_by=name&amp;amp;order_by=age&quot;)

# Instead of a record a table can be used as input.
(assert equal ([[key value];
				[items 20]
				[page 1]
				# Value cannot be a list, like with records.
				# So order_by is defined with two rows.
			    [order_by name]
			    [order_by age]
			    [direction desc]] | url build-query)
              &quot;items=20&amp;amp;page=1&amp;amp;order_by=name&amp;amp;order_by=age&amp;amp;direction=desc&quot;)


# Using url build-query with string interpolation to create a URL
# query parameters.
(assert equal $&quot;https://www.mrhaki.com/blog?({query: &#39;Nushell Niceties&#39;} | url build-query)&quot;
              &quot;https://www.mrhaki.com/blog?query=Nushell+Niceties&quot;)

# Alternative to set query parameters in URL
# following https://github.com/nushell/nushell/discussions/15427.
# First parse a string with a URL to a record with url parse,
# update the params key with query parameters
# and transform to URL again with url join.
(assert equal (&quot;https://www.mrhaki.com&quot; | url parse | update params {query: &#39;Nushell Niceties&#39;} | url join)
			  &quot;https://www.mrhaki.com/?query=Nushell+Niceties&quot;)&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.108.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/1820601623915566132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/1820601623915566132'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/11/nushell-niceties-create-query.html' title='Nushell Niceties: Create Query Parameters For URL'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-4300724065172832761</id><published>2025-10-25T09:55:05.645+02:00</published><updated>2025-10-25T09:57:56.524+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.108.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Strings"/><title type='text'>Nushell Niceties: Joining Values Into String</title><content type='html'>&lt;p&gt;The &lt;code&gt;string&lt;/code&gt; module contains a lot of useful commands to work with strings.
If you want to join several values from a list into a single string you can use the &lt;code&gt;str join&lt;/code&gt; command.
The command accepts as argument the character(s) to use for joining the values.
If you don&#39;t specify the character(s) as argument the default value &lt;code&gt;&#39;&#39;&lt;/code&gt; (empty string) is used.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example you can see usages of the &lt;code&gt;str join&lt;/code&gt; command with and without extra arguments:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using str join without argument.
assert equal ([&#39;a&#39; &#39;b&#39; &#39;c&#39;] | str join) &#39;abc&#39;

# str join can be used with an argument to specify
# the character(s) used for joining the values.
assert equal ([&#39;a&#39; &#39;b&#39; &#39;c&#39;] | str join &#39;, &#39;) &#39;a, b, c&#39;
assert equal ([&#39;a&#39; &#39;b&#39; &#39;c&#39;] | str join &#39;:&#39;) &#39;a:b:c&#39;
assert equal ([1 2 3] | str join &#39; x &#39;) &#39;1 x 2 x 3&#39;

# &#39;&#39; is the default join character, but you can set
# it explicitly as well.
assert equal ([&#39;a&#39; &#39;b&#39; &#39;c&#39;] | str join &#39;&#39;) &#39;abc&#39;&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.108.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4300724065172832761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4300724065172832761'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/10/nushell-niceties-joining-values-into.html' title='Nushell Niceties: Joining Values Into String'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-7794359577596031030</id><published>2025-10-08T08:21:00.007+02:00</published><updated>2025-10-08T08:22:14.597+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Nushell"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell 0.107.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Nushell:Niceties"/><category scheme="http://www.blogger.com/atom/ns#" term="NushellNiceties:Network"/><title type='text'>Nushell Niceties: Encoding And Decoding URL</title><content type='html'>&lt;p&gt;Sometimes you want to transform a string value into a URL encoded value, so it can be used as part of a valid URL.
For example a string with spaces or special characters can not be used in a URL as is, but needs to be URL encoded.
Nushell has the &lt;code&gt;url encode&lt;/code&gt; command to achieve this.
You can simple run this command on a string value and the result is a URL encoded value.
With the option &lt;code&gt;--all&lt;/code&gt; or &lt;code&gt;-a&lt;/code&gt; even more special characters like a dot (&lt;code&gt;.&lt;/code&gt;) are encoded.
The input of the command can be a string value or a list of string values.
But it is also possible to use a record or table structure, but then you need to add as extra argument the name or names of the keys or columns of which the string values should be encoded.&lt;br&gt;
Oppossed to URL encoding a value you can also decode a URL encoded value using the &lt;code&gt;url decode&lt;/code&gt; command.
This command doesn&amp;#8217;t have a special option to run.
Just like with the &lt;code&gt;url encode&lt;/code&gt; command the &lt;code&gt;url decode&lt;/code&gt; command works on strings, list of strings, records and tables.
If the input is a record or table the name of key or column of which the values must be decoded must be passed as extra arguments.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example script the &lt;code&gt;url encode&lt;/code&gt; command is used with several input types:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using url encode command to special characters with
# percent-encoded values. For example a space becomes %20.
assert equal (&#39;Nushell rocks.&#39; | url encode) &#39;Nushell%20rocks.&#39;

# Using the option --all (or -a) to encoding also special characters
# like ; and .
assert equal (&#39;Nushell rocks/is awesome.&#39; | url encode --all) &#39;Nushell%20rocks%2Fis%20awesome%2E&#39;


# The url encode command can be used on list of string values and
# each value in the list will be encoded.
(assert equal ([&#39;Nushell rocks&#39; &#39;Nushell is awesome&#39;] | url encode)
              [&#39;Nushell%20rocks&#39; &#39;Nushell%20is%20awesome&#39;])

# The url encode command can be used on a record,
# but then as extra argument the name of the key(s)
# must be given. The value of the key will be encoded.
(assert equal ({message: &#39;Nushell rocks&#39;, type: &#39;simple&#39;} | url encode message type)
	          {message: &#39;Nushell%20rocks&#39;, type: &#39;simple&#39;})

# Also for a table you can use the url encode command.
# An extra argument is needed to specify the column names
# of which the values should be encoded.
let table = [
  [message private];
  [&#39;Nushell rocks!&#39; true]
  [&#39;is awesome&#39; true]
]
(assert equal ($table | url encode --all message)
    		  [
				[message private];
				[&#39;Nushell%20rocks%21&#39; true]
				[&#39;is%20awesome&#39; true]
			  ])&lt;/pre&gt;
&lt;p&gt;In the following examples you can see the &lt;code&gt;url decode&lt;/code&gt; command used:&lt;/p&gt;

&lt;pre class=&quot;brush:nu;&quot;&gt;use std/assert

# Using url encode command to special characters with
# percent-encoded values. For example a space becomes %20.
assert equal (&#39;Nushell%20rocks.&#39; | url decode) &#39;Nushell rocks.&#39;

# Using the option --all (or -a) to encoding also special characters
# like ; and .
assert equal (&#39;Nushell%20rocks%2Fis%20awesome%2E&#39; | url decode) &#39;Nushell rocks/is awesome.&#39;


# The url encode command can be used on list of string values and
# each value in the list will be encoded.
(assert equal ([&#39;Nushell%20rocks&#39; &#39;Nushell%20is%20awesome&#39;] | url decode)
              [&#39;Nushell rocks&#39; &#39;Nushell is awesome&#39;])

# The url encode command can be used on a record,
# but then as extra argument the name of the key(s)
# must be given. The value of the key will be encoded.
(assert equal ({message: &#39;Nushell%20rocks&#39;, type: &#39;simple&#39;} | url decode message type)
	            {message: &#39;Nushell rocks&#39;, type: &#39;simple&#39;})

# Also for a table you can use the url encode command.
# An extra argument is needed to specify the column names
# of which the values should be encoded.
let table = [
  [message private];
  [&#39;Nushell%20rocks%21&#39; true]
  [&#39;is%20awesome&#39; true]
]
(assert equal ($table | url decode message)
    		  [
				[message private];
				[&#39;Nushell rocks!&#39; true]
				[&#39;is awesome&#39; true]
			  ])

# Running encode and decode should return the original string.
assert equal (&#39;Nushell rocks!&#39; | url encode | url decode) &#39;Nushell rocks!&#39;&lt;/pre&gt;
&lt;p&gt;Written with Nushell 0.107.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/7794359577596031030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/7794359577596031030'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/10/nushell-niceties-encoding-and-decoding.html' title='Nushell Niceties: Encoding And Decoding URL'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-9031227272640272339</id><published>2025-10-05T11:48:00.005+02:00</published><updated>2025-10-05T11:49:23.259+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5.0.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy:Goodness"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:Collections"/><title type='text'>Groovy Goodness: Interleaving Elements From Collections</title><content type='html'>&lt;p&gt;Groovy 5 adds the &lt;code&gt;interleave&lt;/code&gt; method to the &lt;code&gt;Iterable&lt;/code&gt; class.
With this method you can interleave elements from two iterables.
The result is a new &lt;code&gt;List&lt;/code&gt; with elements where the first element is the first element of the first iterable and the second element the first element of the second iterable and so on.
The size of the smallest collection is used to keep interleaving elements.
Elements from the larger collection are ignored in the result.&lt;br&gt;
If you want to have the items from the largest collection in the resulting list you can use the value &lt;code&gt;true&lt;/code&gt; as second argument for the &lt;code&gt;interleave&lt;/code&gt; method.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example you can see usages of the &lt;code&gt;interleave&lt;/code&gt; method:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;def numbers = 1..4
def letters = &#39;a&#39;..&#39;d&#39;

// Using the interleave element to combine
// elements from the numbers and letters collection.
assert numbers.interleave(letters) ==
  [1, &#39;a&#39;, 2, &#39;b&#39;, 3, &#39;c&#39;, 4, &#39;d&#39;]

// The size of the collection with the least elements
// determines the size of the resulting collection.
assert (1..3).interleave(letters) ==
  [1, &#39;a&#39;, 2, &#39;b&#39;, 3, &#39;c&#39;]

// With the extra argument set to `true` the elements
// from the biggest collection are appended to the result.
assert (1..10).interleave(letters, true) ==
    [1, &#39;a&#39;, 2, &#39;b&#39;, 3, &#39;c&#39;, 4, &#39;d&#39;, 5, 6, 7, 8, 9, 10]&lt;/pre&gt;
&lt;p&gt;Written with Groovy 5.0.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/9031227272640272339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/9031227272640272339'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/10/groovy-goodness-interleaving-elements.html' title='Groovy Goodness: Interleaving Elements From Collections'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-6204201372009286729</id><published>2025-09-26T13:23:00.006+02:00</published><updated>2025-09-27T12:16:35.930+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5.0.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy:Goodness"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:Collections"/><title type='text'>Groovy Goodness: Grouping Iterables Using zip And zipAll</title><content type='html'>&lt;p&gt;Groovy 5 adds the extension methods &lt;code&gt;zip&lt;/code&gt; and &lt;code&gt;zipAll&lt;/code&gt; for iterables and iterators.
Using the method you can combine elements from two collections into a new collection.
The new collection contains &lt;code&gt;Tuple2&lt;/code&gt; instances where the values come from the items at the same index from both collections.
So the first item of the first collection is grouped with the first item of the second collection.
The size of the resulting collection is determined by the size of the smallest collection that is &lt;em&gt;zipped&lt;/em&gt;.&lt;br&gt;
With the &lt;code&gt;zipAll&lt;/code&gt; method you can combine iterables of different sizes and set default values for missing items.
It is possible to set a default value if an item is missing from the first iterable or the second iterable.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example several uses of the &lt;code&gt;zip&lt;/code&gt; and &lt;code&gt;zipAll&lt;/code&gt; methods are shown:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;import static groovy.lang.Tuple.tuple

def numbers = 1..4
def letters = &#39;a&#39;..&#39;d&#39;

// Equals size iterables result in same number
// of Tuple2 instances.
assert numbers.zip(letters) == [tuple(1, &#39;a&#39;),
                                tuple(2, &#39;b&#39;),
                                tuple(3, &#39;c&#39;),
                                tuple(4, &#39;d&#39;)]

// With uneven sized iterables the one with least number
// of items is used to determine the number of Tuple2 instances
// in the result.
assert (1..3).zip(letters) == [tuple(1, &#39;a&#39;),
                               tuple(2, &#39;b&#39;),
                               tuple(3, &#39;c&#39;)]

assert numbers.zip(&#39;a&#39;..&#39;c&#39;) == [tuple(1, &#39;a&#39;),
                                 tuple(2, &#39;b&#39;),
                                 tuple(3, &#39;c&#39;)]

// If the iterables are not the same size
// you can use zipAll and define values to
// use to fill up the result.
assert (1..3).zipAll(letters, &#39;default&#39;, _) == [tuple(1, &#39;a&#39;),
                                                tuple(2, &#39;b&#39;),
                                                tuple(3, &#39;c&#39;),
                                                tuple(&#39;default&#39;, &#39;d&#39;)]
assert numbers.zipAll(&#39;a&#39;..&#39;c&#39;, _, &#39;default&#39;) == [tuple(1, &#39;a&#39;),
                                                  tuple(2, &#39;b&#39;),
                                                  tuple(3, &#39;c&#39;),
                                                  tuple(4, &#39;default&#39;)]

// The zip and zipAll also work for iterators.
// The result is a ZipIterator instance so you need
// to materialize the iterator to get the actual result.
assert numbers.iterator().zip(letters.iterator())
                         .toList() == [tuple(1, &#39;a&#39;),
                                       tuple(2, &#39;b&#39;),
                                       tuple(3, &#39;c&#39;),
                                       tuple(4, &#39;d&#39;)]
assert (1..3).iterator().zipAll(letters.iterator(), &#39;default&#39;, _)
                        .toList() == [tuple(1, &#39;a&#39;),
                                      tuple(2, &#39;b&#39;),
                                      tuple(3, &#39;c&#39;),
                                      tuple(&#39;default&#39;, &#39;d&#39;)]&lt;/pre&gt;
&lt;p&gt;Written with Groovy 5.0.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6204201372009286729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6204201372009286729'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/09/groovy-goodness-grouping-iterables.html' title='Groovy Goodness: Grouping Iterables Using zip And zipAll'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-6290549093369834419</id><published>2025-09-17T21:26:00.003+02:00</published><updated>2025-09-17T21:26:52.870+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5.0.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy:Goodness"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:Files"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:GDK"/><title type='text'>Groovy Goodness: Getting Extension And BaseName For File And Path</title><content type='html'>&lt;p&gt;Groovy 5 adds the extension methods &lt;code&gt;getExtension&lt;/code&gt; and &lt;code&gt;getBaseName&lt;/code&gt; to the &lt;code&gt;File&lt;/code&gt; and &lt;code&gt;Path&lt;/code&gt; classes.
You can invoke them as properties for a &lt;code&gt;File&lt;/code&gt; and &lt;code&gt;Path&lt;/code&gt; objects.
Also the &lt;code&gt;asBoolean&lt;/code&gt; method is added.
This mean you can use a &lt;code&gt;File&lt;/code&gt; or &lt;code&gt;Path&lt;/code&gt; instance in a boolean context.
If the underlying file exists &lt;code&gt;true&lt;/code&gt; is returned and &lt;code&gt;false&lt;/code&gt; otherwise.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;The following example shows how to use the new extension methods for the &lt;code&gt;File&lt;/code&gt; class:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;def file = new File(&quot;sample.txt&quot;)

assert file.extension == &#39;txt&#39;
assert file.baseName == &#39;sample&#39;
assert !file  // File doesn&#39;t exist.

// Create file by writing content.
file.write(&quot;Some content&quot;)
assert file // File does exist now.&lt;/pre&gt;
&lt;p&gt;The same extension methods are added to the &lt;code&gt;Path&lt;/code&gt; class and the following example shows their usage:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;import java.nio.file.Path

def path = Path.of(&quot;sample.text&quot;)

assert path.extension == &#39;text&#39;
assert path.baseName == &#39;sample&#39;
assert !path  // Path does not exist.

// Create path by setting content.
path.text = &quot;Some content&quot;
assert path  // Now path exists.&lt;/pre&gt;
&lt;p&gt;Written with Groovy 5.0.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6290549093369834419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6290549093369834419'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/09/groovy-goodness-getting-extension-and.html' title='Groovy Goodness: Getting Extension And BaseName For File And Path'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-6812637766193438241</id><published>2025-09-12T07:57:00.002+02:00</published><updated>2025-09-12T08:08:59.971+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5.0.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy:Goodness"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:GDK"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:RegularExpresssions"/><title type='text'>Groovy Goodness: Accessing Regular Expression Named Groups By Name</title><content type='html'>&lt;p&gt;Groovy (and Java) support using names for groups in regular expressions.
The name of the group is defined using the syntax &lt;code&gt;?&amp;lt;name&amp;gt;&lt;/code&gt; where &lt;code&gt;name&lt;/code&gt; must be replaced with the actual group name.
This is very useful, because you can use the group name to access the value that is &lt;em&gt;captured&lt;/em&gt; by the defined regular expression in a &lt;code&gt;java.util.regex.Matcher&lt;/code&gt; object.
Groovy supports for a long time accessing a group &lt;a href=&quot;https://blog.mrhaki.com/2009/09/groovy-goodness-matchers-for-regular.html&quot;&gt;using the index operator&lt;/a&gt;.
Since Groovy 5 you can use the name of the group to access the value as well.
You can specify the name between square brackets (&lt;code&gt;[&amp;lt;name&amp;gt;]&lt;/code&gt;) or use the name as property.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example a &lt;code&gt;Matcher&lt;/code&gt; is created based on a &lt;code&gt;Pattern&lt;/code&gt; with named groups and the value of the groups is fetched using the name of the group:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;// Using named groups language and framework
// in the regular expression of the Pattern.
def group = (&#39;groovy and grails&#39; =~ /(?&amp;lt;language&amp;gt;\w+) and (?&amp;lt;framework&amp;gt;\w+)/)

// Access group using index operator.
assert group[0][1] == &#39;groovy&#39;

// Since Groovy 5 you can use the name
// of the group to access the value
// with the index operator.
assert group[0][&#39;language&#39;] == &#39;groovy&#39;

// Or use group name as property.
assert group[0].language == &#39;groovy&#39;


assert group[0][2] == &#39;grails&#39;
assert group[0][&#39;framework&#39;] == &#39;grails&#39;
assert group[0].framework == &#39;grails&#39;&lt;/pre&gt;
&lt;p&gt;Written with Groovy 5.0.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6812637766193438241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6812637766193438241'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/09/groovy-goodness-accessing-regular.html' title='Groovy Goodness: Accessing Regular Expression Named Groups By Name'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-6752491054087237424</id><published>2025-09-11T17:28:00.003+02:00</published><updated>2025-09-12T08:09:39.569+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="Java 21"/><category scheme="http://www.blogger.com/atom/ns#" term="Java 9"/><category scheme="http://www.blogger.com/atom/ns#" term="Java:Joy"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaJoy:Language"/><title type='text'>Java Joy: Return Default Value For Null Value</title><content type='html'>&lt;p&gt;Since Java 9 the methods &lt;code&gt;requireNonNullElse&lt;/code&gt; and &lt;code&gt;requireNonNullElseGet&lt;/code&gt; are part of the &lt;code&gt;java.util.Objects&lt;/code&gt; class.
The method &lt;code&gt;requireNonNullElse&lt;/code&gt; accepts two arguments.
The first argument is an object that will be returned if that object is not &lt;code&gt;null&lt;/code&gt;.
The second argument is an object to be returned when the first argument is &lt;code&gt;null&lt;/code&gt;.
With this method you can replace the following ternary operator &lt;code&gt;if (a != null) ? a : b&lt;/code&gt; (or &lt;code&gt;if (a == null) ? b : a)&lt;/code&gt; with &lt;code&gt;Objects.requireNonNullElse(a, b)&lt;/code&gt;.&lt;br&gt;
The method &lt;code&gt;requireNonNullElseGet&lt;/code&gt; allows to define a &lt;code&gt;Supplier&lt;/code&gt; function as second argument.
The &lt;code&gt;Supplier&lt;/code&gt; is only invoked when the first argument is &lt;code&gt;null&lt;/code&gt;, so it is lazy and will only be invoked when the first argument is &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example the methods are invoked with different arguments that are &lt;code&gt;null&lt;/code&gt; and not &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;pre class=&quot;brush:java;&quot;&gt;package mrhaki;

import static java.util.Objects.requireNonNullElse;
import static java.util.Objects.requireNonNullElseGet;

public class NullDefault {
  public static void main(String[] args) {
    // The first argument of requireNonNullElse is returned
    // if the value is not null.
    assert requireNonNullElse(&quot;Hello&quot;, &quot;Hi&quot;).equals(&quot;Hello&quot;);
    // The second argument of requireNonNullElse is returned
    // if the first argument is null.
    assert requireNonNullElse(null, &quot;Hi&quot;).equals(&quot;Hi&quot;);

    // You can also use a Supplier function to calculate the value
    // to be returned if the first argument is null.
    // This function is only invoked when the first argument is null.
    // Very useful if calculating the value is resource intensive.
    assert requireNonNullElseGet(&quot;Hello&quot;, () -&amp;gt; &quot;Hi&quot;).equals(&quot;Hello&quot;);
    assert requireNonNullElseGet(null, () -&amp;gt; &quot;Hi&quot;).equals(&quot;Hi&quot;);
  }
}&lt;/pre&gt;
&lt;p&gt;Written with Java 21.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6752491054087237424'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/6752491054087237424'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/09/java-joy-return-default-value-for-null.html' title='Java Joy: Return Default Value For Null Value'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-3257532396788855237</id><published>2025-09-10T12:50:00.003+02:00</published><updated>2025-09-12T08:08:59.971+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5.0.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy:Goodness"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:Collections"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:Streams"/><title type='text'>Groovy Goodness: Using Index Operator For Streams</title><content type='html'>&lt;p&gt;Groovy already has a lot of extra methods for &lt;code&gt;Stream&lt;/code&gt; classes.
With Groovy 5 the &lt;code&gt;getAt&lt;/code&gt; method is added as a terminal operation.
You can now use the &lt;code&gt;[]&lt;/code&gt; syntax to get an item from a &lt;code&gt;Stream&lt;/code&gt; from a specific position.
The argument is the index of the item or a range with the start and end index of the items to get.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example code the &lt;code&gt;getAt&lt;/code&gt; method and &lt;code&gt;[]&lt;/code&gt; operator are used:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;import java.util.stream.Stream

def list = [&#39;Groovy&#39;, &#39;5&#39;, &#39;is&#39;, &#39;awesome&#39;]

// Use index operator.
// This is a terminal operation for the stream.
assert list.stream()[0] == &#39;Groovy&#39;
assert list.stream().getAt(0) == &#39;Groovy&#39;

// Using a range as argument is possible.
assert list.stream()[2..3] == [&#39;is&#39;, &#39;awesome&#39;]
assert list.stream().getAt(2..3) == [&#39;is&#39;, &#39;awesome&#39;]

assert list.stream()[0..&amp;lt;2] == [&#39;Groovy&#39;, &#39;5&#39;]&lt;/pre&gt;
&lt;p&gt;Written with Groovy 5.0.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/3257532396788855237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/3257532396788855237'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/09/groovy-goodness-using-index-operator.html' title='Groovy Goodness: Using Index Operator For Streams'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-4729534164155822749</id><published>2025-09-09T10:14:00.000+02:00</published><updated>2025-09-12T08:08:59.971+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5.0.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy:Goodness"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:String"/><title type='text'>Groovy Goodness: Get Next And Previous Characters</title><content type='html'>&lt;p&gt;Since Groovy 1.8.2 the &lt;code&gt;next&lt;/code&gt; and &lt;code&gt;previous&lt;/code&gt; methods are added to the &lt;code&gt;Character&lt;/code&gt; class.
When you invoke the method &lt;code&gt;next&lt;/code&gt; on a &lt;code&gt;char&lt;/code&gt; or &lt;code&gt;Character&lt;/code&gt; instance the next character is returned.
And when you use the &lt;code&gt;previous&lt;/code&gt; method the previous character is returned.&lt;br&gt;
Groovy 5 adds an overloaded version of the &lt;code&gt;next&lt;/code&gt; and &lt;code&gt;previous&lt;/code&gt; method that accepts an &lt;code&gt;int&lt;/code&gt; argument.
With this argument you can specify the number of characters to skip before returning the next or previous character. For example &lt;code&gt;&#39;a&#39;.next(2)&lt;/code&gt; return &lt;code&gt;&#39;c&#39;&lt;/code&gt; and &lt;code&gt;&#39;c&#39;.previous(2)&lt;/code&gt; returns &lt;code&gt;&#39;a&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example the &lt;code&gt;next&lt;/code&gt; and &lt;code&gt;previous&lt;/code&gt; methods are used:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;assert &#39;a&#39;.next() == &#39;b&#39;

// You can specify an integer argument since Groovy 5.
assert &#39;a&#39;.next(1) == &#39;b&#39;
assert &#39;a&#39;.next(5) == &#39;f&#39;

// The next method can also be applied
// to a String value where the last
// character is used to get the next character.
assert &#39;bot&#39;.next(4) == &#39;box&#39;

assert &#39;b&#39;.previous() == &#39;a&#39;

// You can specify an integer argument since Groovy 5.
assert &#39;f&#39;.previous(5) == &#39;a&#39;

// The previous method can also be applied
// to a String value where the last
// character is used to get the previous character.
assert &#39;blog&#39;.previous(5) == &#39;blob&#39;&lt;/pre&gt;
&lt;p&gt;Written with Groovy 5.0.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4729534164155822749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4729534164155822749'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/09/groovy-goodness-get-next-and-previous.html' title='Groovy Goodness: Get Next And Previous Characters'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-4047197301147227826</id><published>2025-09-05T20:29:00.005+02:00</published><updated>2025-09-12T08:08:59.971+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5.0.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy:Goodness"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:AST"/><title type='text'>Groovy Goodness: Map Methods To Operators</title><content type='html'>&lt;p&gt;Groovy supports &lt;a href=&quot;https://groovy-lang.org/operators.html#Operator-Overloading&quot;&gt;operator overloading&lt;/a&gt; since the start.
Operator overloading is implemented by an actual method signature that maps to an operator.
For example an object with a &lt;code&gt;plus&lt;/code&gt; method can be used with the &lt;code&gt;+&lt;/code&gt; operator.
There is a &lt;a href=&quot;https://groovy-lang.org/operators.html#Operator-Overloading&quot;&gt;list of methods and operators&lt;/a&gt; available on the Groovy website.
&lt;br /&gt;
As long as an object has a method with a name that Groovy understands the corresponding operator can be used in Groovy code.
This is even true for Java objects.
Since Groovy 5 you can use the &lt;code&gt;groovy.transform.OperatorRename&lt;/code&gt; annotation on classes, methods or constructors to map other method names to the Groovy operator overloading method names.
This is very useful for third-party classes that you cannot change, but still want to use simple operators in Groovy code.
You can reassign a method name to the following methods so an operator can be used: &lt;code&gt;plus&lt;/code&gt;, &lt;code&gt;minus&lt;/code&gt;, &lt;code&gt;multiply&lt;/code&gt;, &lt;code&gt;div&lt;/code&gt;, &lt;code&gt;remainder&lt;/code&gt;, &lt;code&gt;power&lt;/code&gt;, &lt;code&gt;leftShift&lt;/code&gt;, &lt;code&gt;rightShift&lt;/code&gt;, &lt;code&gt;rightShiftUnassigned&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt;, &lt;code&gt;or&lt;/code&gt;, &lt;code&gt;xor&lt;/code&gt;, &lt;code&gt;compareTo&lt;/code&gt;.
  Suppose you use a class with an &lt;code&gt;add&lt;/code&gt; method and want to use the &lt;code&gt;+&lt;/code&gt; operator for this method.
  The following annotation can be used &lt;code&gt;@OperatorRename(plus = &#39;add&#39;)&lt;/code&gt; for a method and inside the method you can use the &lt;code&gt;+&lt;/code&gt; operator instead of the &lt;code&gt;add&lt;/code&gt; method of the class.&lt;/p&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example the methods &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;subtract&lt;/code&gt; and &lt;code&gt;divide&lt;/code&gt; from the class &lt;code&gt;org.javamoney.moneta.Money&lt;/code&gt; are mapped to the &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt; operators:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;@Grab(group=&#39;org.javamoney&#39;, module=&#39;moneta&#39;, version=&#39;1.4.5&#39;, type=&#39;pom&#39;)
import org.javamoney.moneta.Money // Java library to work with Money objects.
import groovy.transform.OperatorRename

// In the scope of this method several methods are
// mapped to operators.
@OperatorRename(
  plus=&#39;add&#39;, // map add method of Money to + operator
  minus=&#39;subtract&#39;, // map subtract method to - operator
  div=&#39;divide&#39; // map divide method to / operator
)
void workWithMoney() {
  def _10Euro = Money.of(10, &quot;EUR&quot;)
  def _20Euro = Money.of(20, &quot;EUR&quot;)

  // First we use the add method of Money.
  assert _10Euro.add(_20Euro) == Money.of(30, &quot;EUR&quot;)

  // Because we redefined the add method to plus
  // we can use the + operator as well in this method.
  assert _10Euro + _20Euro == Money.of(30, &quot;EUR&quot;)


  // Using the subtract method of Money to subtract
  // two Money objects.
  assert _20Euro.subtract(_10Euro) == _10Euro

  // The subtract method is redefined to minus,
  // so we can use the - operator as well.
  assert _20Euro - _10Euro == _10Euro


  // The Money class already has a multiply method
  // that will automatically map to the * operator.
  assert _10Euro.multiply(2) == _20Euro
  assert _10Euro * 2 == _20Euro


  // To divide a Money object the divide method is
  // redefined to the / operator.
  assert _20Euro.divide(2) == _10Euro
  assert _20Euro / 2 == _10Euro
}

workWithMoney()&lt;/pre&gt;
&lt;p&gt;Written with Groovy 5.0.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4047197301147227826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/4047197301147227826'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/09/groovy-goodness-map-methods-to-operators.html' title='Groovy Goodness: Map Methods To Operators'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-6671019398434141469.post-3863334413344468378</id><published>2025-09-04T08:08:00.002+02:00</published><updated>2025-09-12T08:08:59.971+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy 5.0.0"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy:Goodness"/><category scheme="http://www.blogger.com/atom/ns#" term="GroovyGoodness:Operators"/><title type='text'>Groovy Goodness: Logical Implication Operator</title><content type='html'>&lt;p&gt;Since &lt;a href=&quot;https://blog.mrhaki.com/2012/09/groovy-goodness-boolean-implications.html&quot;&gt;Groovy 1.8.3&lt;/a&gt; Groovy has an &lt;code&gt;implies()&lt;/code&gt; method for &lt;code&gt;Boolean&lt;/code&gt; types.
Groovy 5 adds an operator &lt;code&gt;==&amp;gt;&lt;/code&gt; for this method so you have a shorter way to express a logical implication.
A &lt;a href=&quot;https://en.wikiversity.org/wiki/Logical_implication&quot;&gt;logical implication&lt;/a&gt; is a logical function that can be expressed as &lt;em&gt;P &lt;code&gt;==&amp;gt;&lt;/code&gt; Q&lt;/em&gt;.
You can read this as &lt;em&gt;P&lt;/em&gt; implies &lt;em&gt;Q&lt;/em&gt; or if &lt;em&gt;P&lt;/em&gt; than &lt;em&gt;Q&lt;/em&gt;.
This expression is true in every case except when &lt;em&gt;P&lt;/em&gt; is true, but &lt;em&gt;Q&lt;/em&gt; is false.
The following truth table shows the interpretaton of the logical implication operator:&lt;/p&gt;
&lt;table&gt;
&lt;colgroup&gt;
&lt;col style=&quot;width: 33.3333%;&quot;&gt;
&lt;col style=&quot;width: 33.3333%;&quot;&gt;
&lt;col style=&quot;width: 33.3334%;&quot;&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot; halign-left valign-top&quot;&gt;&lt;em&gt;P&lt;/em&gt;&lt;/th&gt;
&lt;th class=&quot; halign-left valign-top&quot;&gt;&lt;em&gt;Q&lt;/em&gt;&lt;/th&gt;
&lt;th class=&quot; halign-left valign-top&quot;&gt;&lt;em&gt;P &lt;code&gt;==&amp;gt;&lt;/code&gt; Q&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;false&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;true&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;true&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;false&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;false&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;true&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;true&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;true&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;true&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;true&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;false&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class=&quot; halign-left valign-top&quot;&gt;&lt;p class=&quot;&quot;&gt;&lt;code&gt;false&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;p&gt;In the following example you can see usage of the new operator:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;// Logical implication operator
assert false ==&amp;gt; true
assert false ==&amp;gt; false
assert true ==&amp;gt; true
assert !(true ==&amp;gt; false)

// Method with the following logical implication:
// If x &amp;gt; 0 then y must be greater than x.
def positiveXimpliesYgreaterX(x, y){
  (x &amp;gt; 0) ==&amp;gt; y &amp;gt; x
}

assert positiveXimpliesYgreaterX(1, 2)
assert !positiveXimpliesYgreaterX(1, 1)
assert positiveXimpliesYgreaterX(0, 1)
assert positiveXimpliesYgreaterX(0, 0)&lt;/pre&gt;
&lt;p&gt;The new operator is very useful in combination with Groovy Contracts:&lt;/p&gt;

&lt;pre class=&quot;brush:groovy;&quot;&gt;import groovy.contracts.Ensures
import org.apache.groovy.contracts.PostconditionViolation
import static groovy.test.GroovyAssert.shouldFail

// Ensures that method result is greater
// than argument x if x &amp;gt; 0 using the
// implies operator.
@Ensures({ x &amp;gt; 0 ==&amp;gt; result &amp;gt; x })
int incrementIfPositive(int x) {
  if (x &amp;gt; 0) {
    // Implementation is following the
    // the logical implication defined in
    // the @Ensures annotation.
    return x + 1
  } else {
    return x
  }
}

// Ensures that method result is greater
// than argument x if x &amp;gt; 0 using the
// implies operator.
@Ensures({ x &amp;gt; 0 ==&amp;gt; result &amp;gt; x })
int faultyIncrementIfPositive(int x) {
  if (x &amp;gt; 0) {
    // Implementation is not following the
    // the logical implication defined in
    // the @Ensures annotation.
    return x - 1
  } else {
    return x
  }
}

// main method to invoke methods annotated with @Ensures.
def main() {
  assert incrementIfPositive(1) == 2
  assert incrementIfPositive(0) == 0

  assert faultyIncrementIfPositive(0) == 0
  shouldFail(PostconditionViolation) {
    faultyIncrementIfPositive(1)
  }
}&lt;/pre&gt;
&lt;p&gt;Written with Groovy 5.0.0.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/3863334413344468378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6671019398434141469/posts/default/3863334413344468378'/><link rel='alternate' type='text/html' href='http://blog.mrhaki.com/2025/09/groovy-goodness-logical-implication.html' title='Groovy Goodness: Logical Implication Operator'/><author><name>Hubert Klein Ikkink</name><uri>http://www.blogger.com/profile/04670461522955162252</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>