Repository: jtablesaw/tablesaw
Pull request #536 (2019-06-07 16:18:34)

Option for setting csv read quote escape char
Thanks for contributing.

- [X] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

Closes https://github.com/jtablesaw/tablesaw/issues/535


Patch:
diff --git a/core/src/main/java/tech/tablesaw/api/DoubleColumn.java b/core/src/main/java/tech/tablesaw/api/DoubleColumn.java
index 95481cf3..67f0b062 100644
--- a/core/src/main/java/tech/tablesaw/api/DoubleColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/DoubleColumn.java
@@ -30,7 +30,6 @@ import java.util.function.DoublePredicate;
 import java.util.function.DoubleSupplier;
 import java.util.function.Function;
 import java.util.function.Predicate;
-import java.util.function.ToDoubleFunction;
 
 public class DoubleColumn extends NumberColumn<Double> implements NumberFillers<DoubleColumn> {
 
@@ -340,24 +339,6 @@ public class DoubleColumn extends NumberColumn<Double> implements NumberFillers<
         return set(row, doubleColumn.getDouble(sourceRow));
     }
 
-    /**
-     * Maps the function across all rows, appending the results to a new NumberColumn
-     *
-     * @param fun function to map
-     * @return the NumberColumn with the results
-     */
-    public DoubleColumn map(ToDoubleFunction<Double> fun) {
-        DoubleColumn result = DoubleColumn.create(name());
-        for (double t : this) {
-            try {
-                result.append(fun.applyAsDouble(t));
-            } catch (Exception e) {
-                result.appendMissing();
-            }
-        }
-        return result;
-    }
-
     /**
      * Returns a new NumberColumn with only those rows satisfying the predicate
      *
diff --git a/core/src/main/java/tech/tablesaw/api/NumericColumn.java b/core/src/main/java/tech/tablesaw/api/NumericColumn.java
index 04546882..473faeee 100644
--- a/core/src/main/java/tech/tablesaw/api/NumericColumn.java
+++ b/core/src/main/java/tech/tablesaw/api/NumericColumn.java
@@ -284,7 +284,7 @@ public interface NumericColumn<T> extends Column<T>, NumberMapFunctions, NumberF
      * @param into Column to which results are appended
      * @return the provided Column, to which results are appended
      */
-    default <R> Column<R> mapInto(DoubleFunction<? extends R> fun, Column<R> into) {
+    default <R extends Column<RT>, RT> R mapInto(DoubleFunction<? extends RT> fun, R into) {
         for (int i = 0; i < size(); i++) {
             try {
                 into.append(fun.apply(getDouble(i)));
diff --git a/core/src/main/java/tech/tablesaw/api/Table.java b/core/src/main/java/tech/tablesaw/api/Table.java
index afd3d94c..44c8c257 100644
--- a/core/src/main/java/tech/tablesaw/api/Table.java
+++ b/core/src/main/java/tech/tablesaw/api/Table.java
@@ -669,11 +669,33 @@ public class Table extends Relation implements Iterable<Row> {
         return where(selection);
     }
 
+    /**
+     * Retains the first rowCount rows if rowCount positive.
+     * Retains the last rowCount rows if rowCount negative.
+     */
+    public Table inRange(int rowCount) {
+        Preconditions.checkArgument(rowCount <= rowCount());
+        int rowStart = rowCount >= 0 ? 0 : rowCount() + rowCount;
+        int rowEnd = rowCount >= 0 ? rowCount : rowCount();
+        return where(Selection.withRange(rowStart, rowEnd));
+    }
+
     public Table inRange(int rowStart, int rowEnd) {
         Preconditions.checkArgument(rowEnd <= rowCount());
         return where(Selection.withRange(rowStart, rowEnd));
     }
 
+    /**
+     * Drops the first rowCount rows if rowCount positive.
+     * Drops the last rowCount rows if rowCount negative.
+     */
+    public Table dropRange(int rowCount) {
+        Preconditions.checkArgument(rowCount <= rowCount());
+        int rowStart = rowCount >= 0 ? rowCount : 0;
+        int rowEnd = rowCount >= 0 ? rowCount() : rowCount() + rowCount;
+        return where(Selection.withRange(rowStart, rowEnd));
+    }
+
     public Table dropRange(int rowStart, int rowEnd) {
         Preconditions.checkArgument(rowEnd <= rowCount());
         return where(Selection.withoutRange(0, rowCount(), rowStart, rowEnd));
diff --git a/core/src/main/java/tech/tablesaw/io/csv/CsvReadOptions.java b/core/src/main/java/tech/tablesaw/io/csv/CsvReadOptions.java
index 9f25dca0..3348c6a3 100644
--- a/core/src/main/java/tech/tablesaw/io/csv/CsvReadOptions.java
+++ b/core/src/main/java/tech/tablesaw/io/csv/CsvReadOptions.java
@@ -31,6 +31,7 @@ public class CsvReadOptions extends ReadOptions {
 
     private final ColumnType[] columnTypes;
     private final Character separator;
+    private final Character quoteChar;
     private final String lineEnding;
     private final Integer maxNumberOfColumns;
     private final Character commentPrefix;
@@ -41,6 +42,7 @@ public class CsvReadOptions extends ReadOptions {
 
         columnTypes = builder.columnTypes;
         separator = builder.separator;
+        quoteChar = builder.quoteChar;
         lineEnding = builder.lineEnding;
         maxNumberOfColumns = builder.maxNumberOfColumns;
         commentPrefix = builder.commentPrefix;
@@ -108,6 +110,10 @@ public class CsvReadOptions extends ReadOptions {
         return separator;
     }
 
+    public Character quoteChar() {
+        return quoteChar;
+    }
+
     public String lineEnding() {
         return lineEnding;
     }
@@ -131,6 +137,7 @@ public class CsvReadOptions extends ReadOptions {
     public static class Builder extends ReadOptions.Builder {
 
         private Character separator = ',';
+        private Character quoteChar;
         private String lineEnding;
         private ColumnType[] columnTypes;
         private Integer maxNumberOfColumns = 10_000;
@@ -167,6 +174,11 @@ public class CsvReadOptions extends ReadOptions {
             return this;
         }
 
+        public Builder quoteChar(Character quoteChar) {
+            this.quoteChar = quoteChar;
+            return this;
+        }
+
         public Builder lineEnding(String lineEnding) {
             this.lineEnding = lineEnding;
             this.lineSeparatorDetectionEnabled = false;
diff --git a/core/src/main/java/tech/tablesaw/io/csv/CsvReader.java b/core/src/main/java/tech/tablesaw/io/csv/CsvReader.java
index 0c080b11..0b06fafe 100644
--- a/core/src/main/java/tech/tablesaw/io/csv/CsvReader.java
+++ b/core/src/main/java/tech/tablesaw/io/csv/CsvReader.java
@@ -159,6 +159,9 @@ public class CsvReader extends FileReader implements DataReader<CsvReadOptions>
 
     private CsvFormat csvFormat(CsvReadOptions options) {
         CsvFormat format = new CsvFormat();
+        if (options.quoteChar() != null) {
+            format.setQuote(options.quoteChar());
+        }
         if (options.separator() != null) {
             format.setDelimiter(options.separator());
         }
diff --git a/core/src/main/java/tech/tablesaw/io/csv/CsvWriteOptions.java b/core/src/main/java/tech/tablesaw/io/csv/CsvWriteOptions.java
index e088052d..b3188829 100644
--- a/core/src/main/java/tech/tablesaw/io/csv/CsvWriteOptions.java
+++ b/core/src/main/java/tech/tablesaw/io/csv/CsvWriteOptions.java
@@ -13,16 +13,16 @@ public class CsvWriteOptions extends WriteOptions {
 
     private final boolean header;
     private final char separator;
-    private final char quotechar;
-    private final char escapechar;
+    private final char quoteChar;
+    private final char escapeChar;
     private final String lineEnd;
 
     private CsvWriteOptions(Builder builder) {
         super(builder);
         this.header = builder.header;
         this.separator = builder.separator;
-        this.quotechar = builder.quoteChar;
-        this.escapechar = builder.escapeChar;
+        this.quoteChar = builder.quoteChar;
+        this.escapeChar = builder.escapeChar;
         this.lineEnd = builder.lineEnd;
     }
 
@@ -35,11 +35,11 @@ public class CsvWriteOptions extends WriteOptions {
     }
 
     public char escapeChar() {
-        return escapechar;
+        return escapeChar;
     }
 
     public char quoteChar() {
-        return quotechar;
+        return quoteChar;
     }
 
     public String lineEnd() {


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/api/NumberColumnTest.java b/core/src/test/java/tech/tablesaw/api/NumberColumnTest.java
index b0bce44a..2d641924 100644
--- a/core/src/test/java/tech/tablesaw/api/NumberColumnTest.java
+++ b/core/src/test/java/tech/tablesaw/api/NumberColumnTest.java
@@ -664,22 +664,29 @@ public class NumberColumnTest {
         assertFalse(DoubleColumn.create("t1", new double[] {1, 0, -1}).noneMatch(isNegativeD));
     }
 
-    private <T> void check(Column<T> column, @SuppressWarnings("unchecked") T... ts) {
-        assertEquals(ts.length, column.size());
-        for (int i = 0; i < ts.length; i++) {
-            assertEquals(ts[i], column.get(i));
+    private <T> void assertColumnContentsEquals(T[] expected, Column<T> column) {
+        assertEquals(expected.length, column.size());
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals(expected[i], column.get(i));
         }
     }
 
     @Test
     public void testFilter() {
         Column<Double> filtered = DoubleColumn.create("t1", new double[] {-1, 0, 1}).filter(isPositiveOrZeroD);
-        check(filtered, 0.0, 1.0);
+        assertColumnContentsEquals(new Double[] { 0.0, 1.0 }, filtered);
+    }
+
+    @Test
+    public void testMap() {
+        DoubleColumn mapped = DoubleColumn.create("t1", new double[] {-1, 0, 1}).map(x -> x * 2.0 + 1.0);
+        assertColumnContentsEquals(new Double[] { -1.0, 1.0, 3.0 }, mapped);
     }
 
     @Test
     public void testMapInto() {
-        check(DoubleColumn.create("t1", new double[] {-1, 0, 1}).mapInto(toStringD, StringColumn.create("result")), "-1.0", "0.0", "1.0");
+        StringColumn mapped = DoubleColumn.create("t1", new double[] {-1, 0, 1}).mapInto(toStringD, StringColumn.create("result"));
+        assertColumnContentsEquals(new String[] { "-1.0", "0.0", "1.0" }, mapped);
     }
 
     @Test
diff --git a/core/src/test/java/tech/tablesaw/api/TableTest.java b/core/src/test/java/tech/tablesaw/api/TableTest.java
index 09e7ce84..2b44bf5d 100644
--- a/core/src/test/java/tech/tablesaw/api/TableTest.java
+++ b/core/src/test/java/tech/tablesaw/api/TableTest.java
@@ -535,6 +535,54 @@ public class TableTest {
         }
     }
 
+    @Test
+    public void dropRangeStarting() throws IOException {
+        Table table = Table.read().csv(CsvReadOptions.builder("../data/bush.csv"));
+        Table result = table.dropRange(20);
+        assertEquals(table.rowCount() - 20, result.rowCount());
+        for (Column<?> c: result.columns()) {
+            for (int r = 0; r < result.rowCount(); r++) {
+                assertEquals(result.getString(r, c.name()), table.getString(r + 20, c.name()));
+            }
+        }
+    }
+
+    @Test
+    public void dropRangeEnding() throws IOException {
+        Table table = Table.read().csv(CsvReadOptions.builder("../data/bush.csv"));
+        Table result = table.dropRange(-20);
+        assertEquals(table.rowCount() - 20, result.rowCount());
+        for (Column<?> c: result.columns()) {
+            for (int r = 0; r < result.rowCount(); r++) {
+                assertEquals(result.getString(r, c.name()), table.getString(r, c.name()));
+            }
+        }
+    }    
+
+    @Test
+    public void inRangeStarting() throws IOException {
+        Table table = Table.read().csv(CsvReadOptions.builder("../data/bush.csv"));
+        Table result = table.inRange(20);
+        assertEquals(20, result.rowCount());
+        for (Column<?> c: result.columns()) {
+            for (int r = 0; r < result.rowCount(); r++) {
+                assertEquals(result.getString(r, c.name()), table.getString(r, c.name()));
+            }
+        }
+    }
+
+    @Test
+    public void inRangeEnding() throws IOException {
+        Table table = Table.read().csv(CsvReadOptions.builder("../data/bush.csv"));
+        Table result = table.inRange(-20);
+        assertEquals(20, result.rowCount());
+        for (Column<?> c: result.columns()) {
+            for (int r = 0; r < result.rowCount(); r++) {
+                assertEquals(result.getString(r, c.name()), table.getString(table.rowCount() - 20 + r, c.name()));
+            }
+        }
+    }  
+
     private DoubleColumn sum(DoubleColumn... columns) {
         int size = columns[0].size();
         DoubleColumn result = DoubleColumn.create("sum", size);