Repository: jtablesaw/tablesaw
Pull request #599 (2019-08-15 18:42:24)

Implementation change of AbstractColumn.asStringColumn
Thanks for contributing.

- [X] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

- asStringColumn now uses delegation to the source column getUnformattedString in order to manage missing values
- Added javadoc

## Testing

- Added a couple of tests for DoubleColumn, IntColumn and DateTimeColumn with missing values


lwhite1: sorry, @ccleva. The tools are there, they're just not obvious. I had to go looking myself to remember the methods. This area needs a bit of reflection at some point so it can be made more obvious.   You can do what you need by implementing the conversion method as : 

```
    StringColumn sc = StringColumn.create(name() + " strings");
    for (int i = 0; i < size(); i++) {
      if (isMissing(i)) {
        sc.appendMissing();
      } else {
        sc.append(getString(i));
      }
    }
    return sc;
  }

```
and actually, getString() alone would probably work without the missing value check because it should return an empty string for missing values, which StringColumn treats as missing.  

If you wouldn't mind revising your commit along these lines it would be appreciated.
ccleva: Sorry @lwhite1 I don't understand what you mean: do you want me to replace getUnformattedString by getString ? This would change the behavior of asStringColumn because all getString methods use the column formatter while the current implementation use String.valueOf. getUnformattedString is doing just that but checking for missing value in addition.

Thanks for taking the time to help me understand. I will fix the Codacy check and the merge conflict in the meantime.
ccleva: No need to apologize @lwhite1, I hope you could finish your project on time.

I made a couple of performance tests: while there is a slight advantage in caching the size on the first call, once the JIT comes into play there is no measurable difference. I modified the PR accordingly.

Thanks

Patch:
diff --git a/core/src/main/java/tech/tablesaw/columns/AbstractColumn.java b/core/src/main/java/tech/tablesaw/columns/AbstractColumn.java
index f2656ed3..ecc2e543 100644
--- a/core/src/main/java/tech/tablesaw/columns/AbstractColumn.java
+++ b/core/src/main/java/tech/tablesaw/columns/AbstractColumn.java
@@ -58,8 +58,8 @@ public abstract class AbstractColumn<T> implements Column<T> {
   @Override
   public StringColumn asStringColumn() {
     StringColumn sc = StringColumn.create(name() + " strings");
-    for (T value : this) {
-      sc.append(String.valueOf(value));
+    for(int i = 0; i < size(); i++) {
+    	sc.append(getUnformattedString(i));
     }
     return sc;
   }
diff --git a/core/src/main/java/tech/tablesaw/columns/Column.java b/core/src/main/java/tech/tablesaw/columns/Column.java
index 5dc97534..21780391 100644
--- a/core/src/main/java/tech/tablesaw/columns/Column.java
+++ b/core/src/main/java/tech/tablesaw/columns/Column.java
@@ -617,5 +617,10 @@ public interface Column<T> extends Iterable<T>, Comparator<T> {
     return where(selectNRowsAtRandom(tableSize, size()));
   }
 
+  /**
+   * Returns a StringColumn consisting of the (unformatted) String representation of this column values
+   * @return a {@link StringColumn} built using the column {@link #getUnformattedString} method
+   */
   StringColumn asStringColumn();
+
 }


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/api/DateTimeColumnTest.java b/core/src/test/java/tech/tablesaw/api/DateTimeColumnTest.java
index fc8b56e7..fdde6a14 100644
--- a/core/src/test/java/tech/tablesaw/api/DateTimeColumnTest.java
+++ b/core/src/test/java/tech/tablesaw/api/DateTimeColumnTest.java
@@ -24,6 +24,8 @@ import java.time.format.DateTimeFormatter;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import tech.tablesaw.columns.strings.StringColumnType;
+
 public class DateTimeColumnTest {
 
   private DateTimeColumn column1;
@@ -109,4 +111,15 @@ public class DateTimeColumnTest {
 	  assertEquals("2000-01-01T00:00:00", column1.getString(0));
 	  assertEquals("NaT", column1.getString(1));
   }
+
+  @Test
+  public void testAsStringColumn() {
+    column1.appendCell("1923-10-20T10:15:30");
+    column1.appendMissing();
+    StringColumn sc = column1.asStringColumn();
+    assertEquals("Game date strings", sc.name());
+    assertEquals(2, sc.size());
+    assertEquals("1923-10-20T10:15:30.000", sc.get(0));
+    assertEquals(StringColumnType.missingValueIndicator(), sc.get(1));
+  }
 }
diff --git a/core/src/test/java/tech/tablesaw/api/NumberColumnTest.java b/core/src/test/java/tech/tablesaw/api/NumberColumnTest.java
index 599bf8aa..b540613d 100644
--- a/core/src/test/java/tech/tablesaw/api/NumberColumnTest.java
+++ b/core/src/test/java/tech/tablesaw/api/NumberColumnTest.java
@@ -48,6 +48,7 @@ import org.junit.jupiter.api.Test;
 import tech.tablesaw.columns.Column;
 import tech.tablesaw.columns.numbers.DoubleColumnType;
 import tech.tablesaw.columns.numbers.NumberColumnFormatter;
+import tech.tablesaw.columns.strings.StringColumnType;
 import tech.tablesaw.selection.Selection;
 
 /** Unit tests for the NumberColumn class */
@@ -319,6 +320,28 @@ public class NumberColumnTest {
     assertEquals("48392.2932", sc.get(0));
   }
 
+  @Test
+  public void testDoubleColumnAsStringWithMissing() {
+    DoubleColumn numberColumn = DoubleColumn.create("test");
+    numberColumn.append(48392.2932);
+    numberColumn.appendMissing();
+    StringColumn sc = numberColumn.asStringColumn();
+    assertEquals("test strings", sc.name());
+    assertEquals("48392.2932", sc.get(0));
+    assertEquals(StringColumnType.missingValueIndicator(), sc.get(1));
+  }
+
+  @Test
+  public void testIntegerColumnAsStringWithMissing() {
+    IntColumn numberColumn = IntColumn.create("test");
+    numberColumn.append(48392);
+    numberColumn.appendMissing();
+    StringColumn sc = numberColumn.asStringColumn();
+    assertEquals("test strings", sc.name());
+    assertEquals("48392", sc.get(0));
+    assertEquals(StringColumnType.missingValueIndicator(), sc.get(1));
+  }
+
   @Test
   public void testNumberFormat3() {
     DoubleColumn numberColumn = DoubleColumn.create("test");