Repository: jtablesaw/tablesaw (java)

Issue:
Table.compareRows not working as expected
The static method `Table.compareRows` always returns `false` except for the last row. It happens even when we compare rows of the same table. 
```
Table table = Table.read().csv(new File("data.csv"));
System.out.println(table.shape());

for(int i = 0; i < table.rowCount(); i++) {
    System.out.println("ROW " + i + ": "+ Table.compareRows(i, table, table));
}
```
Actual output:
```
data.csv: 5 rows X 2 cols
ROW 0: false
ROW 1: false
ROW 2: false
ROW 3: false
ROW 4: true
```
Expected output:
```
data.csv: 5 rows X 2 cols
ROW 0: true
ROW 1: true
ROW 2: true
ROW 3: true
ROW 4: true
```

Pull request:
Fix, document, and test ColumnType.compare and Table.compareRows
Thanks for contributing.

- [x] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

- ColumnType.compare now compares rows at the same rownumber instead of comparing with the last row of the other column
- Table.compareRows now returns false if the number of columns is different in both tables
- Added missing javadoc on both methods

Fixes #1229

## Testing

Yes, let me know if you want them moved to an existing test class


Patch:
diff --git a/core/src/main/java/tech/tablesaw/api/ColumnType.java b/core/src/main/java/tech/tablesaw/api/ColumnType.java
index 0ca61683..12d22b7a 100644
--- a/core/src/main/java/tech/tablesaw/api/ColumnType.java
+++ b/core/src/main/java/tech/tablesaw/api/ColumnType.java
@@ -82,16 +82,11 @@ public interface ColumnType {
   /** TODO: Research this method to provide a good comment */
   AbstractColumnParser<?> customParser(ReadOptions options);
 
-  /** TODO: Research this method to provide a good comment */
-  default boolean compare(int rowNumber, Column<?> temp, Column<?> original) {
-    Object o1 = original.get(rowNumber);
-    Object o2 = temp.get(temp.size() - 1);
-    return o1 == null ? o2 == null : o1.equals(o2);
-  }
-
   /**
    * Returns true if the value at the specified index in column1 is equal to the value at the
    * specified index in column 2
+   *
+   * @throws {@code IndexOutOfBoundsException} if either index exceeds the corresponding column size
    */
   default boolean compare(int col1Row, Column<?> col1, int col2Row, Column<?> col2) {
     Object o1 = col1.get(col1Row);
diff --git a/core/src/main/java/tech/tablesaw/api/Table.java b/core/src/main/java/tech/tablesaw/api/Table.java
index f507749c..5cd84250 100644
--- a/core/src/main/java/tech/tablesaw/api/Table.java
+++ b/core/src/main/java/tech/tablesaw/api/Table.java
@@ -529,17 +529,22 @@ public class Table extends Relation implements Iterable<Row> {
   }
 
   /**
-   * Returns {@code true} if the row @rowNumber in table1 holds the same data as the row at
-   * rowNumber in table2
+   * Returns {@code true} if the row {@code rowNumber} in {@code table1} holds the same values than
+   * the row at {@code rowNumber} in {@code table2}. Returns {@code false} if the number of columns
+   * is different in the two tables.
+   *
+   * @throws {@code IndexOutOfBoundsException} if {@code rowNumber} exceeds either table number of
+   *     rows
    */
   public static boolean compareRows(int rowNumber, Table table1, Table table2) {
-    int columnCount = table1.columnCount();
-    boolean result;
+    final int columnCount = table1.columnCount();
+    if (columnCount != table2.columnCount()) {
+      return false;
+    }
     for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
       ColumnType columnType = table1.column(columnIndex).type();
-      result =
-          columnType.compare(rowNumber, table2.column(columnIndex), table1.column(columnIndex));
-      if (!result) {
+      if (!columnType.compare(
+          rowNumber, table2.column(columnIndex), rowNumber, table1.column(columnIndex))) {
         return false;
       }
     }


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/api/TableTest.java b/core/src/test/java/tech/tablesaw/api/TableTest.java
index d659a0ea..51c8b90e 100644
--- a/core/src/test/java/tech/tablesaw/api/TableTest.java
+++ b/core/src/test/java/tech/tablesaw/api/TableTest.java
@@ -50,6 +50,7 @@ public class TableTest {
   private static final ColumnType[] BUSH_COLUMN_TYPES = {LOCAL_DATE, INTEGER, STRING};
   private static Table bush;
   private static Table bushMinimized;
+  private static Table missingValues;
 
   private Table table;
   private final DoubleColumn f1 = DoubleColumn.create("f1");
@@ -64,6 +65,8 @@ public class TableTest {
                     .columnTypes(BUSH_COLUMN_TYPES));
     ColumnType[] types = {LOCAL_DATE, SHORT, STRING};
     bushMinimized = Table.read().csv(CsvReadOptions.builder("../data/bush.csv").columnTypes(types));
+    missingValues = Table.read().csv(CsvReadOptions.builder("../data/missing_values.csv")
+      .missingValueIndicator("-"));
   }
 
   @BeforeEach
@@ -916,4 +919,36 @@ public class TableTest {
       fail("toString shouldn't throw " + e);
     }
   }
+
+  @Test
+  void testCompareRowsIdentical() {
+      for(int i = 0; i < missingValues.rowCount(); i++) {
+          assertTrue(Table.compareRows(i, missingValues, missingValues), "Row " + i + " is not equal to itself");
+      }
+  }
+
+  @Test
+  void testCompareRowsDifferent() {
+      Table differentTable = missingValues.copy().sortDescendingOn("Sales");
+      for(int i = 0; i < missingValues.rowCount(); i++) {
+          assertFalse(Table.compareRows(i, missingValues, differentTable), "Row " + i + " is equal to a different row");
+      }
+  }
+
+  @Test
+  void testCompareRowsDifferentColumns() {
+      Table differentTable = missingValues.copy().removeColumns("Sales");
+      for(int i = 0; i < missingValues.rowCount(); i++) {
+          assertFalse(Table.compareRows(i, missingValues, differentTable), "Row " + i + " is equal to a row with less columns");
+      }
+  }
+  
+  @Test
+  void testCompareRowsOutOfBound() {
+      Table differentTable = missingValues.copy().dropRows(0);
+      int lastRowNumber = missingValues.rowCount() - 1;
+      assertThrows(IndexOutOfBoundsException.class,
+          () -> Table.compareRows(lastRowNumber, missingValues, differentTable),
+          "Row outside range does not throw exception");
+  }
 }