public FrameBlock(List<ValueType> schema, List<String> names, String[][] data) {
   _numRows = 0; // maintained on append
   _schema = new ArrayList<ValueType>(schema);
   _colnames = new ArrayList<String>(names);
   _coldata = new ArrayList<Array>();
   for (int i = 0; i < data.length; i++) appendRow(data[i]);
 }
  /**
   * Appends the given argument frameblock 'that' to this frameblock by creating a deep copy to
   * prevent side effects. For cbind, the frames are appended column-wise (same number of rows),
   * while for rbind the frames are appended row-wise (same number of columns).
   *
   * @param that
   * @param ret
   * @param cbind
   * @return
   */
  public FrameBlock appendOperations(FrameBlock that, FrameBlock ret, boolean cbind)
      throws DMLRuntimeException {
    if (cbind) // COLUMN APPEND
    {
      // sanity check row dimension mismatch
      if (getNumRows() != that.getNumRows()) {
        throw new DMLRuntimeException(
            "Incompatible number of rows for cbind: "
                + that.getNumRows()
                + " (expected: "
                + getNumRows()
                + ")");
      }

      // allocate output frame
      if (ret == null) ret = new FrameBlock();
      ret._numRows = _numRows;

      // concatenate schemas (w/ deep copy to prevent side effects)
      ret._schema = new ArrayList<ValueType>(_schema);
      ret._schema.addAll(that._schema);
      ret._colnames = new ArrayList<String>(_colnames);
      ret._colnames.addAll(that._colnames);

      // concatenate column data (w/ deep copy to prevent side effects)
      for (Array tmp : _coldata) ret._coldata.add(tmp.clone());
      for (Array tmp : that._coldata) ret._coldata.add(tmp.clone());
    } else // ROW APPEND
    {
      // sanity check column dimension mismatch
      if (getNumColumns() != that.getNumColumns()) {
        throw new DMLRuntimeException(
            "Incompatible number of columns for rbind: "
                + that.getNumColumns()
                + " (expected: "
                + getNumColumns()
                + ")");
      }

      // allocate output frame (incl deep copy schema)
      if (ret == null) ret = new FrameBlock();
      ret._numRows = _numRows;
      ret._schema = new ArrayList<ValueType>(_schema);
      ret._colnames = new ArrayList<String>(_colnames);

      // concatenate data (deep copy first, append second)
      for (Array tmp : _coldata) ret._coldata.add(tmp.clone());
      Iterator<Object[]> iter = that.getObjectRowIterator();
      while (iter.hasNext()) ret.appendRow(iter.next());
    }

    return ret;
  }