Repository: jtablesaw/tablesaw
Pull request #705 (2019-10-30 19:55:56)

Updates the Column.mapInto javadoc and adds two new Column.map methods
Thanks for contributing.

- [x] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

This fixed issue #704 by updating the `Column.mapInto` javadoc.

It also adds two new `Column.map` methods which offer a way to append data to a newly created column. The output column is easily created/provided via a `Supplier` or `Function`.

## Testing

Yes.


emilianbold: Ooh, interestingly `NumericColumn` has a separate `mapInto` that does `append`!
benmccann: Let's remove the `NumericColumn` version then so it inherits the main one

I don't know that we need the new methods because it should be pretty easy to use the existing methods to accomplish the same thing. E.g. the user could just create the column with an initial size instead of empty and use `mapTo`
emilianbold: To me something like `StringColumn s = d.map(String::valueOf, StringColumn::create)` looks in line with other stream APIs, eg. [`Stream.toArray(int[]::new)`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/Stream.html#toArray(java.util.function.IntFunction) )

Right now `d.mapInto(String::valueOf, StringColumn.create(d.name(), d.size()))` works but I dislike the manual setting of the size (and, partially, the setting of the name).

I'll remove the `NumericColumn` method too.
benmccann: Ok. Makes sense. At least for the one taking a creator. What's the use case for the one taking a supplier?
emilianbold: Actually, the `NumericColumn.mapInto` takes a `DoubleFunction` so we can't remove it. We could add the same logic (`set` instead of `append`) but it might break existing code...
emilianbold: > What's the use case for the one taking a supplier?

Good point, if we have the creator it's enough.
benmccann: The new creator method let's you do `StringColumn s = d.map(String::valueOf, StringColumn::create`.  But actually, don't we already have a method that lets you do `StringColumn s = d.map(String::valueOf)`?
emilianbold: > But actually, don't we already have a method that lets you do `StringColumn s = d.map(String::valueOf)`?

Not as far as I can tell. If `d` is a `DoubleColumn` then `map` will create another `DoubleColumn`. You cannot change the column type with a `map`, only `mapInto`.

Patch:
diff --git a/core/src/main/java/tech/tablesaw/columns/Column.java b/core/src/main/java/tech/tablesaw/columns/Column.java
index 7f8b459e..c6f5b0ca 100644
--- a/core/src/main/java/tech/tablesaw/columns/Column.java
+++ b/core/src/main/java/tech/tablesaw/columns/Column.java
@@ -25,6 +25,7 @@ import java.util.Optional;
 import java.util.function.BinaryOperator;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 import tech.tablesaw.api.ColumnType;
 import tech.tablesaw.api.StringColumn;
 import tech.tablesaw.api.Table;
@@ -327,11 +328,13 @@ public interface Column<T> extends Iterable<T>, Comparator<T> {
   }
 
   /**
-   * Maps the function across all rows, appending the results to the provided Column
+   * Maps the function across all rows, storing the results into the provided Column.
+   *
+   * <p>The target column must have at least the same number of rows.
    *
    * @param fun function to map
-   * @param into Column to which results are appended
-   * @return the provided Column, to which results are appended
+   * @param into Column into which results are set
+   * @return the provided Column
    */
   default <R, C extends Column<R>> C mapInto(Function<? super T, ? extends R> fun, C into) {
     for (int i = 0; i < size(); i++) {
@@ -344,6 +347,34 @@ public interface Column<T> extends Iterable<T>, Comparator<T> {
     return into;
   }
 
+  /**
+   * Maps the function across all rows, appending the results to the created Column.
+   *
+   * <p>Example:
+   *
+   * <pre>
+   * DoubleColumn d;
+   * StringColumn s = d.map(String::valueOf, StringColumn::create);
+   * </pre>
+   *
+   * @param fun function to map
+   * @param creator the creator of the Column. Its String argument will be the name of the current
+   *     column (see {@link #name()})
+   * @return the Column with the results
+   */
+  default <R, C extends Column<R>> C map(
+      Function<? super T, ? extends R> fun, Function<String, C> creator) {
+    C into = creator.apply(name());
+    for (int i = 0; i < size(); i++) {
+      if (isMissing(i)) {
+        into.appendMissing();
+      } else {
+        into.append(fun.apply(get(i)));
+      }
+    }
+    return into;
+  }
+
   Column<T> setMissing(int i);
 
   /**


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/columns/ColumnTest.java b/core/src/test/java/tech/tablesaw/columns/ColumnTest.java
index 0ce3f478..f89efd04 100644
--- a/core/src/test/java/tech/tablesaw/columns/ColumnTest.java
+++ b/core/src/test/java/tech/tablesaw/columns/ColumnTest.java
@@ -278,6 +278,14 @@ public class ColumnTest {
         DoubleColumn.create("t1", new double[] {-1, 0, 1}).map(negate), 1.0, -0.0, -1.0);
   }
 
+  @Test
+  public void testMap2() {
+    StringColumn c =
+        DoubleColumn.create("t1", new double[] {-1, 0, 1})
+            .map(String::valueOf, StringColumn::create);
+    assertContentEquals(c, "-1.0", "0.0", "1.0");
+  }
+
   @Test
   public void testMaxComparator() {
     assertEquals(