diff --git a/pkg/converters/datatable/sub_basic_data_table.go b/pkg/converters/datatable/sub_basic_data_table.go new file mode 100644 index 00000000..e72cc03d --- /dev/null +++ b/pkg/converters/datatable/sub_basic_data_table.go @@ -0,0 +1,87 @@ +package datatable + +// SubBasicDataTable defines the structure of sub basic data table +type SubBasicDataTable struct { + baseTable BasicDataTable + fromIndex int + toIndex int +} + +// SubBasicDataTableRowIterator defines the structure of sub basic data table row iterator +type SubBasicDataTableRowIterator struct { + dataTable *SubBasicDataTable + innerIterator BasicDataTableRowIterator + currentIndex int +} + +// DataRowCount returns the total count of data row +func (t *SubBasicDataTable) DataRowCount() int { + return t.toIndex - t.fromIndex +} + +// HeaderColumnNames returns the header column name list +func (t *SubBasicDataTable) HeaderColumnNames() []string { + return t.baseTable.HeaderColumnNames() +} + +// DataRowIterator returns the iterator of data row +func (t *SubBasicDataTable) DataRowIterator() BasicDataTableRowIterator { + innerIterator := t.baseTable.DataRowIterator() + currentIndex := -1 + + // skip rows until reaching the fromIndex + for currentIndex = -1; currentIndex < t.fromIndex-1 && innerIterator.HasNext(); currentIndex++ { + innerIterator.Next() + } + + return &SubBasicDataTableRowIterator{ + dataTable: t, + innerIterator: innerIterator, + currentIndex: currentIndex, + } +} + +// HasNext returns whether the iterator does not reach the end +func (t *SubBasicDataTableRowIterator) HasNext() bool { + return t.currentIndex+1 < t.dataTable.toIndex && t.innerIterator.HasNext() +} + +// CurrentRowId returns current row id +func (t *SubBasicDataTableRowIterator) CurrentRowId() string { + return t.innerIterator.CurrentRowId() +} + +// Next returns the next basic data row +func (t *SubBasicDataTableRowIterator) Next() BasicDataTableRow { + if t.currentIndex+1 >= t.dataTable.toIndex { + return nil + } + + t.currentIndex++ + return t.innerIterator.Next() +} + +// CreateSubBasicTable returns a sub basic data table that references a portion of the original table +func CreateSubBasicTable(dataTable BasicDataTable, fromIndex, toIndex int) *SubBasicDataTable { + if fromIndex < 0 { + fromIndex = 0 + } + + if fromIndex > dataTable.DataRowCount() { + fromIndex = dataTable.DataRowCount() + } + + if toIndex > dataTable.DataRowCount() { + toIndex = dataTable.DataRowCount() + } + + if toIndex < fromIndex { + toIndex = fromIndex + } + + return &SubBasicDataTable{ + baseTable: dataTable, + fromIndex: fromIndex, + toIndex: toIndex, + } +} diff --git a/pkg/converters/datatable/sub_basic_data_table_test.go b/pkg/converters/datatable/sub_basic_data_table_test.go new file mode 100644 index 00000000..89a134e3 --- /dev/null +++ b/pkg/converters/datatable/sub_basic_data_table_test.go @@ -0,0 +1,140 @@ +package datatable + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateSubBasicTable_WithValidInput(t *testing.T) { + columns := []string{"Col1", "Col2", "Col3"} + rows := []*testBasicDataTableRow{ + { + rowId: "1", + rowColumns: []string{"A1", "B1", "C1"}, + }, + { + rowId: "2", + rowColumns: []string{"A2", "B2", "C2"}, + }, + { + rowId: "3", + rowColumns: []string{"A3", "B3", "C3"}, + }, + } + + basicDataTable := &testBasicDataTable{ + headerColumns: columns, + rows: rows, + } + + subTable := CreateSubBasicTable(basicDataTable, 1, 2) + assert.Equal(t, 1, subTable.DataRowCount()) + assert.Equal(t, columns, subTable.HeaderColumnNames()) +} + +func TestCreateSubBasicTable_WithInvalidInput(t *testing.T) { + columns := []string{"Col1", "Col2", "Col3"} + rows := []*testBasicDataTableRow{ + { + rowId: "1", + rowColumns: []string{"A1", "B1", "C1"}, + }, + { + rowId: "2", + rowColumns: []string{"A2", "B2", "C2"}, + }, + } + + basicDataTable := &testBasicDataTable{ + headerColumns: columns, + rows: rows, + } + + subTable := CreateSubBasicTable(basicDataTable, -1, 2) + assert.Equal(t, 0, subTable.fromIndex) + assert.Equal(t, 2, subTable.toIndex) + + subTable = CreateSubBasicTable(basicDataTable, 5, 2) + assert.Equal(t, 2, subTable.fromIndex) + assert.Equal(t, 2, subTable.toIndex) + + subTable = CreateSubBasicTable(basicDataTable, 0, 5) + assert.Equal(t, 0, subTable.fromIndex) + assert.Equal(t, 2, subTable.toIndex) + + subTable = CreateSubBasicTable(basicDataTable, 2, 1) + assert.Equal(t, 2, subTable.fromIndex) + assert.Equal(t, 2, subTable.toIndex) +} + +func TestSubBasicDataTable_DataRowIterator(t *testing.T) { + columns := []string{"Col1", "Col2", "Col3"} + rows := []*testBasicDataTableRow{ + { + rowId: "1", + rowColumns: []string{"A1", "B1", "C1"}, + }, + { + rowId: "2", + rowColumns: []string{"A2", "B2", "C2"}, + }, + { + rowId: "3", + rowColumns: []string{"A3", "B3", "C3"}, + }, + } + + basicDataTable := &testBasicDataTable{ + headerColumns: columns, + rows: rows, + } + + subTable := CreateSubBasicTable(basicDataTable, 1, 3) + iterator := subTable.DataRowIterator() + + assert.True(t, iterator.HasNext()) + firstRow := iterator.Next() + assert.NotNil(t, firstRow) + assert.Equal(t, "2", iterator.CurrentRowId()) + assert.Equal(t, "A2", firstRow.GetData(0)) + assert.Equal(t, "B2", firstRow.GetData(1)) + assert.Equal(t, "C2", firstRow.GetData(2)) + + assert.True(t, iterator.HasNext()) + secondRow := iterator.Next() + assert.NotNil(t, secondRow) + assert.Equal(t, "3", iterator.CurrentRowId()) + assert.Equal(t, "A3", secondRow.GetData(0)) + assert.Equal(t, "B3", secondRow.GetData(1)) + assert.Equal(t, "C3", secondRow.GetData(2)) + + assert.False(t, iterator.HasNext()) + assert.Nil(t, iterator.Next()) +} + +func TestSubBasicDataTable_EmptyDataRange(t *testing.T) { + columns := []string{"Col1", "Col2", "Col3"} + rows := []*testBasicDataTableRow{ + { + rowId: "1", + rowColumns: []string{"A1", "B1", "C1"}, + }, + { + rowId: "2", + rowColumns: []string{"A2", "B2", "C2"}, + }, + } + + basicDataTable := &testBasicDataTable{ + headerColumns: columns, + rows: rows, + } + + subTable := CreateSubBasicTable(basicDataTable, 1, 1) + assert.Equal(t, 0, subTable.DataRowCount()) + + iterator := subTable.DataRowIterator() + assert.False(t, iterator.HasNext()) + assert.Nil(t, iterator.Next()) +}