You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by gi...@apache.org on 2023/02/14 10:44:42 UTC
[groovy-dev-site] branch asf-site updated: 2023/02/14 10:44:39: Generated dev website from groovy-website@1c519a7
This is an automated email from the ASF dual-hosted git repository.
git-site-role pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-dev-site.git
The following commit(s) were added to refs/heads/asf-site by this push:
new dc9cffa 2023/02/14 10:44:39: Generated dev website from groovy-website@1c519a7
dc9cffa is described below
commit dc9cffa14cdb53b24cdf7fb2242207ee425649ef
Author: jenkins <bu...@apache.org>
AuthorDate: Tue Feb 14 10:44:39 2023 +0000
2023/02/14 10:44:39: Generated dev website from groovy-website@1c519a7
---
blog/adventures-with-groovyfx.html | 2 +-
blog/classifying-iris-flowers-with-deep.html | 2 +-
blog/deck-of-cards-with-groovy.html | 2 +-
blog/detecting-objects-with-groovy-the.html | 2 +-
blog/feed.atom | 10 +
blog/fruity-eclipse-collections.html | 2 +-
blog/index.html | 4 +-
blog/reading-and-writing-csv-files.html | 526 +++++++++++++++++++++++++++
blog/whiskey-clustering-with-groovy-and.html | 2 +-
9 files changed, 544 insertions(+), 8 deletions(-)
diff --git a/blog/adventures-with-groovyfx.html b/blog/adventures-with-groovyfx.html
index f911309..ecdcb65 100644
--- a/blog/adventures-with-groovyfx.html
+++ b/blog/adventures-with-groovyfx.html
@@ -53,7 +53,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Adventures with GroovyFX</a></li><li><a href='#_further_information' class='anchor-link'>Further information</a></li></ul></div><div class='col-lg-8 col-lg-pull-0'><a name='doc'></a><h1>Adventures with GroovyFX</h1><p><span>Author: <i>Paul King</i></span><br/><span>Publish [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Adventures with GroovyFX</a></li><li><a href='#_further_information' class='anchor-link'>Further information</a></li></ul><br/><ul class='nav-sidebar'><li style='padding: 0.35em 0.625em; background-color: #eee'><span>Related posts</span></li><li><a href='./reading-and-writ [...]
<div class="sectionbody">
<div class="paragraph">
<p>This blog looks at a <a href="http://groovyfx.org/">GroovyFX</a> version of a <a href="https://donraab.medium.com/my-weird-and-wonderful-first-adventures-with-javafx-6efe3b1923c8">ToDo application originally written in JavaFX</a>.
diff --git a/blog/classifying-iris-flowers-with-deep.html b/blog/classifying-iris-flowers-with-deep.html
index f1e16ea..aa5f9cd 100644
--- a/blog/classifying-iris-flowers-with-deep.html
+++ b/blog/classifying-iris-flowers-with-deep.html
@@ -53,7 +53,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Classifying Iris Flowers with Deep Learning, Groovy and GraalVM</a></li><li><a href='#_deep_learning' class='anchor-link'>Deep Learning</a></li><li><a href='#_encog' class='anchor-link'>Encog</a></li><li><a href='#_eclipse_deeplearning4j' class='anchor-link'>Eclipse DeepLe [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Classifying Iris Flowers with Deep Learning, Groovy and GraalVM</a></li><li><a href='#_deep_learning' class='anchor-link'>Deep Learning</a></li><li><a href='#_encog' class='anchor-link'>Encog</a></li><li><a href='#_eclipse_deeplearning4j' class='anchor-link'>Eclipse DeepLe [...]
<div class="sectionbody">
<div class="paragraph">
<p><span class="image right"><img src="https://blogs.apache.org/groovy/mediaresource/80b1adc8-7234-4046-a279-e272b0a0edd4" alt="iris flowers" width="200"></span>
diff --git a/blog/deck-of-cards-with-groovy.html b/blog/deck-of-cards-with-groovy.html
index 202aa28..e958f42 100644
--- a/blog/deck-of-cards-with-groovy.html
+++ b/blog/deck-of-cards-with-groovy.html
@@ -53,7 +53,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Deck of cards with Groovy, JDK collections and Eclipse Collections</a></li></ul><br/><ul class='nav-sidebar'><li style='padding: 0.35em 0.625em; background-color: #eee'><span>Related posts</span></li><li><a href='./groovy-null-processing'>Groovy Processing Nulls In Lists</ [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Deck of cards with Groovy, JDK collections and Eclipse Collections</a></li></ul><br/><ul class='nav-sidebar'><li style='padding: 0.35em 0.625em; background-color: #eee'><span>Related posts</span></li><li><a href='./groovy-null-processing'>Groovy Processing Nulls In Lists</ [...]
<p>Once again, <a href="https://twitter.com/TheDonRaab">Donald Raab</a> has produced an interesting
<a href="https://donraab.medium.com/how-to-create-a-deck-of-cards-using-eclipse-collections-d9838ac430b7">blog post</a> on
<a href="https://github.com/eclipse/eclipse-collections">Eclipse Collections</a> this one shows some code for modelling and manipulating cards with Java 17 and Eclipse Collections. His related katas are highly recommended.
diff --git a/blog/detecting-objects-with-groovy-the.html b/blog/detecting-objects-with-groovy-the.html
index 32b42f2..26215c7 100644
--- a/blog/detecting-objects-with-groovy-the.html
+++ b/blog/detecting-objects-with-groovy-the.html
@@ -53,7 +53,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Detecting objects with Groovy, the Deep Java Library (DJL), and Apache MXNet</a></li><li><a href='#_deep_learning' class='anchor-link'>Deep Learning</a></li><li><a href='#_deep_java_library_djl_apache_mxnet' class='anchor-link'>Deep Java Library (DJL) & Apache MXNet</a [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Detecting objects with Groovy, the Deep Java Library (DJL), and Apache MXNet</a></li><li><a href='#_deep_learning' class='anchor-link'>Deep Learning</a></li><li><a href='#_deep_java_library_djl_apache_mxnet' class='anchor-link'>Deep Java Library (DJL) & Apache MXNet</a [...]
<div class="sectionbody">
<div class="paragraph">
<p>This blog posts looks at using <a href="https://groovy-lang.org/">Apache Groovy</a> with the
diff --git a/blog/feed.atom b/blog/feed.atom
index 4846d6f..7561c91 100644
--- a/blog/feed.atom
+++ b/blog/feed.atom
@@ -294,6 +294,16 @@
<published>2022-07-24T12:55:23+00:00</published>
<summary>Groovy 4.0.4, 3.0.12 and 2.5.18 Release Announcement.</summary>
</entry>
+ <entry>
+ <author>
+ <name>Paul King</name>
+ </author>
+ <title>Reading and Writing CSV files with Groovy</title>
+ <link href="http://groovy.apache.org/blog/reading-and-writing-csv-files"/>
+ <updated>2022-07-25T14:26:20+00:00</updated>
+ <published>2022-07-25T14:26:20+00:00</published>
+ <summary>This post looks at processing CSV files using OpenCSV, Commons CSV, and Jackson Databind libraries.</summary>
+ </entry>
<entry>
<author>
<name>Paul King</name>
diff --git a/blog/fruity-eclipse-collections.html b/blog/fruity-eclipse-collections.html
index 07099b6..8d14e79 100644
--- a/blog/fruity-eclipse-collections.html
+++ b/blog/fruity-eclipse-collections.html
@@ -53,7 +53,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Fruity Eclipse Collections</a></li><li><a href='#_eclipse_collections_fruit_salad' class='anchor-link'>Eclipse Collections Fruit Salad</a></li><li><a href='#_exploring_emoji_colors' class='anchor-link'>Exploring emoji colors</a></li><li><a href='#_putting_it_all_together' [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Fruity Eclipse Collections</a></li><li><a href='#_eclipse_collections_fruit_salad' class='anchor-link'>Eclipse Collections Fruit Salad</a></li><li><a href='#_exploring_emoji_colors' class='anchor-link'>Exploring emoji colors</a></li><li><a href='#_putting_it_all_together' [...]
<div class="sectionbody">
<div class="paragraph">
<p>This blog post continues on the theme of using fruit emoji from the
diff --git a/blog/index.html b/blog/index.html
index 14239f5..d75c012 100644
--- a/blog/index.html
+++ b/blog/index.html
@@ -53,7 +53,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3' id='blog-index'><ul class='nav-sidebar list'><li class='active'><a href='/blog/'>Blogs</a></li><li><a href='wordle-checker'>Checking Wordle with Groovy</a></li><li><a href='groovy-null-processing'>Groovy Processing Nulls In Lists</a></li><li><a href='groundhog-day'>Groundhog Day</a></li><li><a href='fun-with-rating-stars'>Fun with rating stars</a></li><li><a href=' [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3' id='blog-index'><ul class='nav-sidebar list'><li class='active'><a href='/blog/'>Blogs</a></li><li><a href='wordle-checker'>Checking Wordle with Groovy</a></li><li><a href='groovy-null-processing'>Groovy Processing Nulls In Lists</a></li><li><a href='groundhog-day'>Groundhog Day</a></li><li><a href='fun-with-rating-stars'>Fun with rating stars</a></li><li><a href=' [...]
<div class='row'>
<div class='colset-3-footer'>
<div class='col-1'>
@@ -97,7 +97,7 @@
colors: am5.ColorSet.new(root, {})
}));
wc.data.setAll([
- { category: "groovy", value: 47 }, { category: "release", value: 23 }, { category: "decryption", value: 1 }, { category: "encryption", value: 1 }, { category: "security", value: 1 }, { category: "bouncy castle", value: 1 }, { category: "domain specific languages", value: 1 }, { category: "jsr385", value: 1 }, { category: "units of measurement", value: 1 }, { category: "machine learning", value: 2 }, { category: "artificial intelligence", value: 1 }, { category: "neural ne [...]
+ { category: "groovy", value: 48 }, { category: "release", value: 23 }, { category: "decryption", value: 1 }, { category: "encryption", value: 1 }, { category: "security", value: 1 }, { category: "bouncy castle", value: 1 }, { category: "domain specific languages", value: 1 }, { category: "jsr385", value: 1 }, { category: "units of measurement", value: 1 }, { category: "machine learning", value: 2 }, { category: "artificial intelligence", value: 1 }, { category: "neural ne [...]
]);
wc.labels.template.setAll({
paddingTop: 5,
diff --git a/blog/reading-and-writing-csv-files.html b/blog/reading-and-writing-csv-files.html
new file mode 100644
index 0000000..81b77bc
--- /dev/null
+++ b/blog/reading-and-writing-csv-files.html
@@ -0,0 +1,526 @@
+<!DOCTYPE html>
+<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
+<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
+<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
+<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--><head>
+ <meta charset='utf-8'/><meta http-equiv='X-UA-Compatible' content='IE=edge'/><meta name='viewport' content='width=device-width, initial-scale=1'/><meta name='keywords' content='csv, data, deserialization, files, groovy, reading, records, data science, serialization, writing, opencsv, commons csv, jackson databind, cycling'/><meta name='description' content='This post looks at processing CSV files using OpenCSV, Commons CSV, and Jackson Databind libraries.'/><title>The Apache Groovy p [...]
+</head><body>
+ <div id='fork-me'>
+ <a href='https://github.com/apache/groovy'>
+ <img style='position: fixed; top: 20px; right: -58px; border: 0; z-index: 100; transform: rotate(45deg);' src='/img/horizontal-github-ribbon.png'/>
+ </a>
+ </div><div id='st-container' class='st-container st-effect-9'>
+ <nav class='st-menu st-effect-9' id='menu-12'>
+ <h2 class='icon icon-lab'>Socialize</h2><ul>
+ <li>
+ <a href='http://groovy-lang.org/mailing-lists.html' class='icon'><span class='fa fa-envelope'></span> Discuss on the mailing-list</a>
+ </li><li>
+ <a href='https://twitter.com/ApacheGroovy' class='icon'><span class='fa fa-twitter'></span> Groovy on Twitter</a>
+ </li><li>
+ <a href='http://groovy-lang.org/events.html' class='icon'><span class='fa fa-calendar'></span> Events and conferences</a>
+ </li><li>
+ <a href='https://github.com/apache/groovy' class='icon'><span class='fa fa-github'></span> Source code on GitHub</a>
+ </li><li>
+ <a href='http://groovy-lang.org/reporting-issues.html' class='icon'><span class='fa fa-bug'></span> Report issues in Jira</a>
+ </li><li>
+ <a href='http://stackoverflow.com/questions/tagged/groovy' class='icon'><span class='fa fa-stack-overflow'></span> Stack Overflow questions</a>
+ </li><li>
+ <a href='http://groovycommunity.com/' class='icon'><span class='fa fa-slack'></span> Slack Community</a>
+ </li>
+ </ul>
+ </nav><div class='st-pusher'>
+ <div class='st-content'>
+ <div class='st-content-inner'>
+ <!--[if lt IE 7]>
+ <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
+ <![endif]--><div><div class='navbar navbar-default navbar-static-top' role='navigation'>
+ <div class='container'>
+ <div class='navbar-header'>
+ <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
+ <span class='sr-only'></span><span class='icon-bar'></span><span class='icon-bar'></span><span class='icon-bar'></span>
+ </button><a class='navbar-brand' href='../index.html'>
+ <i class='fa fa-star'></i> Apache Groovy
+ </a>
+ </div><div class='navbar-collapse collapse'>
+ <ul class='nav navbar-nav navbar-right'>
+ <li class=''><a href='http://groovy-lang.org/learn.html'>Learn</a></li><li class=''><a href='http://groovy-lang.org/documentation.html'>Documentation</a></li><li class=''><a href='/download.html'>Download</a></li><li class=''><a href='http://groovy-lang.org/support.html'>Support</a></li><li class=''><a href='/'>Contribute</a></li><li class=''><a href='http://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li class=''><a href='https://groovy.apac [...]
+ <a data-effect='st-effect-9' class='st-trigger' href='#'>Socialize</a>
+ </li><li class=''>
+ <a href='../search.html'>
+ <i class='fa fa-search'></i>
+ </a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Reading and Writing CSV files with Groovy</a></li><li><a href='#_introduction' class='anchor-link'>Introduction</a></li><li><a href='#_commons_csv' class='anchor-link'>Commons CSV</a></li><li><a href='#_opencsv' class='anchor-link'>OpenCSV</a></li><li><a href='#_jackson_da [...]
+<h2 id="_introduction">Introduction</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>In this post, we’ll look at reading and writing CSV
+files using Groovy.</p>
+</div>
+<div class="sect2">
+<h3 id="_arent_csv_files_just_text_files">Aren’t CSV files just text files?</h3>
+<div class="paragraph">
+<p>For simple cases, we can treat CSV files no differently than we
+would other text files. Suppose we have the following data that
+we would like to write to a CSV file:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def data = [
+ ['place', 'firstname', 'lastname', 'team'],
+ ['1', 'Lorena', 'Wiebes', 'Team DSM'],
+ ['2', 'Marianne', 'Vos', 'Team Jumbo Visma'],
+ ['3', 'Lotte', 'Kopecky', 'Team SD Worx']
+]</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Groovy uses <code>File</code> or <code>Path</code> objects similar to Java.
+We’ll use a <code>File</code> object here and, for our purposes, we’ll
+just use a temporary file since we are just going to read it
+back in and check it against our data. Here is how to create
+a temporary file:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def file = File.createTempFile('FemmesStage1Podium', '.csv')</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Writing our CSV (in this simple example) is as simple as joining
+the data with commas and the lines with line separator character(s):</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.text = data*.join(',').join(System.lineSeparator())</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Here we "wrote" the entire file contents in one go but there
+are options for writing a line or character or byte at a time.</p>
+</div>
+<div class="paragraph">
+<p>Reading the data in is just as simple. We read the lines and
+split on commas:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">assert file.readLines()*.split(',') == data</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>In general, we might want to further process the data.
+Groovy provides nice options for this too. Suppose we have
+the following existing CSV file:</p>
+</div>
+<div class="paragraph">
+<p><span class="image"><img src="https://blogs.apache.org/groovy/mediaresource/0128cd28-23e8-42ca-b408-c2eaf1c82d1c" alt="HommesOverall"></span></p>
+</div>
+<div class="paragraph">
+<p>We can read in the file and select various columns of interest
+with code like below:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def file = new File('HommesStageWinners.csv')
+def rows = file.readLines().tail()*.split(',')
+int total = rows.size()
+Set names = rows.collect { it[1] + ' ' + it[2] }
+Set teams = rows*.getAt(3)
+Set countries = rows*.getAt(4)
+String result = "Across $total stages, ${names.size()} riders from " +
+ "${teams.size()} teams and ${countries.size()} countries won stages."
+assert result == 'Across 21 stages, 15 riders from 10 teams and 9 countries won stages.'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Here, the <code>tail()</code> method skips over the header line.
+Column 0 contains the stage number which we ignore.
+Column 1 contains the first name, column 2 the last name,
+column 3 the team, and column 4 the country of the rider.
+We store away the full names, teams and countries in sets
+to remove duplicates. We then create an overall result
+message using the size of those sets.</p>
+</div>
+<div class="paragraph">
+<p>While for this simple example, the coding was fairly simple,
+it isn’t recommended to hand process CSV files in this fashion.
+The details for CSV can quickly get messy. What if the values
+themselves contain commas or newlines? Perhaps we can surround in
+double quotes but then what if the value contains a double quote?
+And so forth. For this reason, CSV libraries are recommended.</p>
+</div>
+<div class="paragraph">
+<p>We’ll look at three shortly, but first let’s summarise some of
+the highlights of the tour by looking at multiple winners.
+Here is some code which summarises our CSV data:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def byValueDesc = { -it.value }
+def bySize = { k, v -> [k, v.size()] }
+def isMultiple = { it.value > 1 }
+def multipleWins = { Closure select -> rows
+ .groupBy(select)
+ .collectEntries(bySize)
+ .findAll(isMultiple)
+ .sort(byValueDesc)
+ .entrySet()
+ .join(', ')
+}
+println 'Multiple wins by country:\n' + multipleWins{ it[4] }
+println 'Multiple wins by rider:\n' + multipleWins{ it[1] + ' ' + it[2] }
+println 'Multiple wins by team:\n' + multipleWins{ it[3] }</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This summary has nothing in particular to do with CSV files but
+is summarised in honour of the great riding during the tour!
+Here’s the output:</p>
+</div>
+<div class="paragraph">
+<p><span class="image"><img src="https://blogs.apache.org/groovy/mediaresource/878dc821-991f-468c-823d-7672ddeccb98" alt="MultipleWins"></span></p>
+</div>
+<div class="paragraph">
+<p>Okay, now let’s look at our three CSV libraries.</p>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_commons_csv">Commons CSV</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>The <a href="https://commons.apache.org/proper/commons-csv/">Apache Commons CSV</a> library makes writing and parsing CSV files easier.
+Here is the code for writing our CSV which makes use of the
+<code>CSVPrinter</code> class:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withWriter { w ->
+ new CSVPrinter(w, CSVFormat.DEFAULT).printRecords(data)
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>And here is the code for reading it back in which uses
+the <code>RFC4180</code> parser factory singleton:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withReader { r ->
+ assert RFC4180.parse(r).records*.toList() == data
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>There are other singleton factories for tab-separated values
+and other common formats and builders to let you set a whole
+variety of options like escape characters, quote options,
+whether to use an enum to define header names, and whether
+to ignore empty lines or nulls.</p>
+</div>
+<div class="paragraph">
+<p>For our more elaborate example, we have a tiny bit more work
+to do. We’ll use the builder to tell the parser to skip the
+header row. We could have chosen to use the <code>tail()</code> trick we
+used earlier, but we decided to use the parser features instead.
+The code would look like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withReader { r ->
+ def rows = RFC4180.builder()
+ .setHeader()
+ .setSkipHeaderRecord(true)
+ .build()
+ .parse(r)
+ .records
+ assert rows.size() == 21
+ assert rows.collect { it.firstname + ' ' + it.lastname }.toSet().size() == 15
+ assert rows*.team.toSet().size() == 10
+ assert rows*.country.toSet().size() == 9
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can see here that we have used column names rather than
+column numbers during our processing. Using column names is
+another advantage of using the CSV library; it would be quite
+a lot of work to do that aspect by hand. Also note that,
+for simplicity, we didn’t create the entire <em>result</em> message
+as in the earlier example. Instead, we just checked the size
+of all the relevant sets that we calculated previously.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_opencsv">OpenCSV</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>The <a href="http://opencsv.sourceforge.net/">OpenCSV</a> library handles
+the messy CSV details when needed but doesn’t get in the way
+for simple cases. For our first example, the <code>CSVReader</code> and
+<code>CSVWriter</code> classes will be suitable. Here is the code for
+writing our CSV file in the same way as earlier:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withWriter { w ->
+ new CSVWriter(w).writeAll(data.collect{ it as String[] })
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>And here is the code for reading data:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withReader { r ->
+ assert new CSVReader(r).readAll() == data
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If we look at the produced file, it is already a little fancier
+than earlier with double quotes around all data:</p>
+</div>
+<div class="paragraph">
+<p><span class="image"><img src="https://blogs.apache.org/groovy/mediaresource/f226d423-3408-4aaa-ba3e-d835e6c894aa" alt="FemmesPodiumStage1"></span></p>
+</div>
+<div class="paragraph">
+<p>If we want to do more elaborate processing, the
+<code>CSVReaderHeaderAware</code> class is aware of the initial header
+row and its column names. Here is our more elaborate example
+which processed some of the data further:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withReader { r ->
+ def rows = []
+ def reader = new CSVReaderHeaderAware(r)
+ while ((next = reader.readMap())) rows << next
+ assert rows.size() == 21
+ assert rows.collect { it.firstname + ' ' + it.lastname }.toSet().size() == 15
+ assert rows*.team.toSet().size() == 10
+ assert rows*.country.toSet().size() == 9
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can see here that we have again used column names rather
+than column numbers during our processing. For simplicity, we
+followed the same style as in the Commons CSV example and just
+checked the size of all the relevant sets that we calculated
+previously.</p>
+</div>
+<div class="paragraph">
+<p>OpenCSV also supports transforming CSV files into JavaBean
+instances. First, we define our target class (or annotate
+an existing domain class):</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">class Cyclist {
+ @CsvBindByName(column = 'firstname')
+ String first
+ @CsvBindByName(column = 'lastname')
+ String last
+ @CsvBindByName
+ String team
+ @CsvBindByName
+ String country
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>For two of the columns, we’ve indicated that the column name in
+the CSV file doesn’t match our class property. The annotation
+attribute caters for that scenario.</p>
+</div>
+<div class="paragraph">
+<p>Then, we can use this code to convert our CSV file into a list
+of domain objects:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withReader { r ->
+ List<Cyclist> rows = new CsvToBeanBuilder(r).withType(Cyclist).build().parse()
+ assert rows.size() == 21
+ assert rows.collect { it.first + ' ' + it.last }.toSet().size() == 15
+ assert rows*.team.toSet().size() == 10
+ assert rows*.country.toSet().size() == 9
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>OpenCSV has many options we didn’t show. When writing files you
+can specify the separator and quote characters, when reading CSV
+you can specify column positions, types, and validate data.</p>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_jackson_databind_csv">Jackson Databind CSV</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>The <a href="https://github.com/FasterXML/jackson-databind">Jackson Databind</a> library supports the <a href="https://github.com/FasterXML/jackson-dataformats-text/tree/master/csv">CSV</a>
+format (as well as many others).</p>
+</div>
+<div class="paragraph">
+<p>Writing CSV files from existing data is simple as shown here
+for running example:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">file.withWriter { w ->
+ new CsvMapper().writeValue(w, data)
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This writes the data into our temporary file as we saw with
+previous examples. One minor difference is that by default,
+just the values containing spaces will be double quoted but
+like the other libraries, there are many configuration options
+to tweak such settings.</p>
+</div>
+<div class="paragraph">
+<p>Reading the data can be achieved using the following code:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def mapper = new CsvMapper().readerForListOf(String).with(CsvParser.Feature.WRAP_AS_ARRAY)
+file.withReader { r ->
+ assert mapper.readValues(r).readAll() == data
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Our more elaborate example is done in a similar way:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def schema = CsvSchema.emptySchema().withHeader()
+def mapper = new CsvMapper().readerForMapOf(String).with(schema)
+file.withReader { r ->
+ def rows = mapper.readValues(r).readAll()
+ assert rows.size() == 21
+ assert rows.collect { it.firstname + ' ' + it.lastname }.toSet().size() == 15
+ assert rows*.team.toSet().size() == 10
+ assert rows*.country.toSet().size() == 9
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Here, we tell the library to make use of our header row and
+store each row of data in a map.</p>
+</div>
+<div class="paragraph">
+<p>Jackson Databind also supports writing to classes including
+JavaBeans as well as records. Let’s create a record to hold
+our cyclist information:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">@JsonCreator
+record Cyclist(
+ @JsonProperty('stage') int stage,
+ @JsonProperty('firstname') String first,
+ @JsonProperty('lastname') String last,
+ @JsonProperty('team') String team,
+ @JsonProperty('country') String country) {
+ String full() { "$first $last" }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Note that again we can indicate where our record component names
+may not match the names used in the CSV file, we simply supply
+the alternate name when specifying the property. There are other
+options like indicating that a field is required or giving its
+column position, but we don’t need those options for our example.
+We’ve also added a <code>full()</code> helper method to return the full
+name of the cyclist.</p>
+</div>
+<div class="paragraph">
+<p>Groovy will use native records on platforms that support it
+(JDK16+) or emulated records on earlier platforms.</p>
+</div>
+<div class="paragraph">
+<p>Now we can write our code for record deserialization:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="prettyprint highlight"><code data-lang="groovy">def schema = CsvSchema.emptySchema().withHeader()
+def mapper = new CsvMapper().readerFor(Cyclist).with(schema)
+file.withReader { r ->
+ List<Cyclist> records = mapper.readValues(r).readAll()
+ assert records.size() == 21
+ assert records*.full().toSet().size() == 15
+ assert records*.team.toSet().size() == 10
+ assert records*.country.toSet().size() == 9
+}</code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_conclusion">Conclusion</h2>
+<div class="sectionbody">
+<div class="paragraph">
+<p>We have looked at writing and reading CSV files to Strings and
+domain classes and records. We had a look at handling simple
+cases by hand and also looked at the OpenCSV, Commons CSV and
+Jackson Databind CSV libraries.</p>
+</div>
+<div class="paragraph">
+<p>Code for these examples:<br>
+<a href="https://github.com/paulk-asert/CsvGroovy" class="bare">https://github.com/paulk-asert/CsvGroovy</a></p>
+</div>
+<div class="paragraph">
+<p>Code for other examples of using Groovy for Data Science:<br>
+<a href="https://github.com/paulk-asert/groovy-data-science" class="bare">https://github.com/paulk-asert/groovy-data-science</a></p>
+</div>
+</div>
+</div></div></div></div></div><footer id='footer'>
+ <div class='row'>
+ <div class='colset-3-footer'>
+ <div class='col-1'>
+ <h1>Groovy</h1><ul>
+ <li><a href='http://groovy-lang.org/learn.html'>Learn</a></li><li><a href='http://groovy-lang.org/documentation.html'>Documentation</a></li><li><a href='/download.html'>Download</a></li><li><a href='http://groovy-lang.org/support.html'>Support</a></li><li><a href='/'>Contribute</a></li><li><a href='http://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li><a href='https://groovy.apache.org/events.html'></a></li>
+ </ul>
+ </div><div class='col-2'>
+ <h1>About</h1><ul>
+ <li><a href='https://github.com/apache/groovy'>Source code</a></li><li><a href='http://groovy-lang.org/security.html'>Security</a></li><li><a href='http://groovy-lang.org/learn.html#books'>Books</a></li><li><a href='http://groovy-lang.org/thanks.html'>Thanks</a></li><li><a href='http://www.apache.org/foundation/sponsorship.html'>Sponsorship</a></li><li><a href='http://groovy-lang.org/faq.html'>FAQ</a></li><li><a href='http://groovy-lang.org/sea [...]
+ </ul>
+ </div><div class='col-3'>
+ <h1>Socialize</h1><ul>
+ <li><a href='http://groovy-lang.org/mailing-lists.html'>Discuss on the mailing-list</a></li><li><a href='https://twitter.com/ApacheGroovy'>Groovy on Twitter</a></li><li><a href='http://groovy-lang.org/events.html'>Events and conferences</a></li><li><a href='https://github.com/apache/groovy'>Source code on GitHub</a></li><li><a href='http://groovy-lang.org/reporting-issues.html'>Report issues in Jira</a></li><li><a href='http://stackoverflow.com [...]
+ </ul>
+ </div><div class='col-right'>
+ <p>
+ The Groovy programming language is supported by the <a href='http://www.apache.org'>Apache Software Foundation</a> and the Groovy community.
+ </p><div text-align='right'>
+ <img src='../img/asf_logo.png' title='The Apache Software Foundation' alt='The Apache Software Foundation' style='width:60%'/>
+ </div><p>Apache® and the Apache feather logo are either registered trademarks or trademarks of The Apache Software Foundation.</p>
+ </div>
+ </div><div class='clearfix'>© 2003-2023 the Apache Groovy project — Groovy is Open Source: <a href='http://www.apache.org/licenses/LICENSE-2.0.html' alt='Apache 2 License'>license</a>, <a href='https://privacy.apache.org/policies/privacy-policy-public.html'>privacy policy</a>.</div>
+ </div>
+ </footer></div>
+ </div>
+ </div>
+ </div>
+ </div><script src='../js/vendor/jquery-1.10.2.min.js' defer></script><script src='../js/vendor/classie.js' defer></script><script src='../js/vendor/bootstrap.js' defer></script><script src='../js/vendor/sidebarEffects.js' defer></script><script src='../js/vendor/modernizr-2.6.2.min.js' defer></script><script src='../js/plugins.js' defer></script><script src='https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.js'></script><script>document.addEventListener('DOMContentLoa [...]
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+ ga('create', 'UA-257558-10', 'auto');
+ ga('send', 'pageview');
+ </script>
+</body></html>
\ No newline at end of file
diff --git a/blog/whiskey-clustering-with-groovy-and.html b/blog/whiskey-clustering-with-groovy-and.html
index b453843..844d712 100644
--- a/blog/whiskey-clustering-with-groovy-and.html
+++ b/blog/whiskey-clustering-with-groovy-and.html
@@ -53,7 +53,7 @@
</ul>
</div>
</div>
- </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Whiskey Clustering with Groovy and Apache Ignite</a></li><li><a href='#_whiskey_clustering' class='anchor-link'>Whiskey Clustering</a></li><li><a href='#_apache_ignite' class='anchor-link'>Apache Ignite</a></li><li><a href='#_implementation_details' class='anchor-link'>Imp [...]
+ </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Whiskey Clustering with Groovy and Apache Ignite</a></li><li><a href='#_whiskey_clustering' class='anchor-link'>Whiskey Clustering</a></li><li><a href='#_apache_ignite' class='anchor-link'>Apache Ignite</a></li><li><a href='#_implementation_details' class='anchor-link'>Imp [...]
<div class="sectionbody">
<div class="paragraph">
<p>In a previous <a href="https://blogs.apache.org/groovy/entry/using-groovy-with-apache-wayang">blog post</a>,