| <!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 programming language - Blogs - Reading and Writing CSV files with Groovy</title><link href='../img/favicon.ico' type='image/x-ico' rel='icon'/><link rel='stylesheet' type='text/css' href='../css/bootstrap.css'/><link rel='stylesheet' type='text/css' href='../css/font-awesome.min.css'/><link rel='stylesheet' type='text/css' href='../css/style.css'/><link rel='stylesheet' type='text/css' href='https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css'/> |
| </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='https://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='https://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='https://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='https://groovy-lang.org/learn.html'>Learn</a></li><li class=''><a href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li class=''><a href='/download.html'>Download</a></li><li class=''><a href='https://groovy-lang.org/support.html'>Support</a></li><li class=''><a href='/'>Contribute</a></li><li class=''><a href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li class=''><a href='/blog'>Blog posts</a></li><li class=''><a href='https://groovy.apache.org/events.html'></a></li><li> |
| <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_databind_csv' class='anchor-link'>Jackson Databind CSV</a></li><li><a href='#_conclusion' class='anchor-link'>Conclusion</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-dauphine'>Processing Results for the Critérium du Dauphiné</a></li><li><a href='./using-groovy-with-apache-wayang'>Using Groovy with Apache Wayang and Apache Spark</a></li><li><a href='./deep-learning-and-eclipse-collections'>Deep Learning and Eclipse Collections</a></li><li><a href='./matrix-calculations-with-groovy-apache'>Matrix calculations with Groovy, Apache Commons Math, ojAlgo, Nd4j and EJML</a></li><li><a href='./deck-of-cards-with-groovy'>Deck of cards with Groovy, JDK collections and Eclipse Collections</a></li><li><a href='./detecting-objects-with-groovy-the'>Detecting objects with Groovy, the Deep Java Library (DJL), and Apache MXNet</a></li><li><a href='./groovy-record-performance'>Groovy Record Performance</a></li><li><a href='./comparators-and-sorting-in-groovy'>Comparators and Sorting in Groovy</a></li><li><a href='./whiskey-clustering-with-groovy-and'>Whiskey Clustering with Groovy and Apache Ignite</a></li><li><a href='./adventures-with-groovyfx'>Adventures with GroovyFX</a></li><li><a href='./groovy-records'>Groovy Records</a></li><li><a href='./fruity-eclipse-collections'>Fruity Eclipse Collections</a></li><li><a href='./classifying-iris-flowers-with-deep'>Classifying Iris Flowers with Deep Learning, Groovy and GraalVM</a></li></ul></div><div class='col-lg-8 col-lg-pull-0'><a name='doc'></a><h1>Reading and Writing CSV files with Groovy</h1><p><span>Author: <i>Paul King</i></span><br/><span>Published: 2022-07-25 02:26PM</span></p><hr/><div class="sect1"> |
| <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="img/HommesOverall.png" 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="img/HommesMultipleWins.png" 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="img/FemmesPodiumStage1.png" 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='https://groovy-lang.org/learn.html'>Learn</a></li><li><a href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li><a href='/download.html'>Download</a></li><li><a href='https://groovy-lang.org/support.html'>Support</a></li><li><a href='/'>Contribute</a></li><li><a href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li><a href='/blog'>Blog posts</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='https://groovy-lang.org/security.html'>Security</a></li><li><a href='https://groovy-lang.org/learn.html#books'>Books</a></li><li><a href='https://groovy-lang.org/thanks.html'>Thanks</a></li><li><a href='http://www.apache.org/foundation/sponsorship.html'>Sponsorship</a></li><li><a href='https://groovy-lang.org/faq.html'>FAQ</a></li><li><a href='https://groovy-lang.org/search.html'>Search</a></li> |
| </ul> |
| </div><div class='col-3'> |
| <h1>Socialize</h1><ul> |
| <li><a href='https://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='https://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='https://groovy-lang.org/reporting-issues.html'>Report issues in Jira</a></li><li><a href='http://stackoverflow.com/questions/tagged/groovy'>Stack Overflow questions</a></li><li><a href='http://groovycommunity.com/'>Slack Community</a></li> |
| </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('DOMContentLoaded',prettyPrint)</script><script> |
| (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> |