/// <summary> /// Decomposes a column into repeat units by analysing the values of the column with increasing index. /// If a column value is repeated, the current range is finalized and a new range is started. At the end, /// a list of index ranges is returned. Inside each range the column values are guaranteed to be unique. /// </summary> /// <param name="col">Column to decompose.</param> /// <returns>List of integer ranges. Inside a single range the column values are ensured to be unique.</returns> public static IList <ContiguousIntegerRange> DecomposeIntoRepeatUnits(DataColumn col) { var result = new List <ContiguousIntegerRange>(); var alreadyIn = new HashSet <AltaxoVariant>(); var currentRangeStart = 0; var currentRangeCount = 0; for (int i = 0; i < col.Count; i++) { if (alreadyIn.Contains(col[i])) { alreadyIn.Clear(); result.Add(ContiguousIntegerRange.FromStartAndCount(currentRangeStart, currentRangeCount)); currentRangeStart = i; currentRangeCount = 0; } alreadyIn.Add(col[i]); currentRangeCount++; } if (currentRangeCount > 0) { result.Add(ContiguousIntegerRange.FromStartAndCount(currentRangeStart, currentRangeCount)); } return(result); }
public void TestFromStartAndCount4A() { Assert.Throws(typeof(ArgumentOutOfRangeException), () => { var r = ContiguousIntegerRange.FromStartAndCount(0, -1); }); }
public void TestFromStartAndCount3() { var r = ContiguousIntegerRange.FromStartAndCount(int.MaxValue, 0); Assert.IsTrue(r.IsEmpty); Assert.AreEqual(0, r.Count); Assert.AreEqual(0, r.LongCount); Assert.AreEqual(0, r.Start); }
public void TestEmpty() { var r = new ContiguousIntegerRange(); Assert.IsTrue(r.IsEmpty); Assert.AreEqual(0, r.Count); Assert.AreEqual(0, r.LongCount); Assert.AreEqual(0, r.Start); }
public void TestFromFirstAndLastInclusive1() { var r = ContiguousIntegerRange.FromFirstAndLastInclusive(int.MinValue + 1, int.MaxValue); Assert.IsFalse(r.IsEmpty); Assert.AreEqual((long)uint.MaxValue, r.LongCount); Assert.AreEqual(int.MinValue + 1, r.Start); Assert.AreEqual(int.MaxValue, r.LastInclusive); }
public void TestFromStartAndEndExclusive4() { var r = ContiguousIntegerRange.FromStartAndEndExclusive(int.MinValue, int.MaxValue); Assert.IsFalse(r.IsEmpty); Assert.AreEqual(uint.MaxValue, r.LongCount); Assert.AreEqual(int.MinValue, r.Start); Assert.AreEqual(int.MaxValue, r.EndExclusive); }
public void TestFromStartAndEndExclusive2() { var r = ContiguousIntegerRange.FromStartAndEndExclusive(int.MinValue, int.MinValue); Assert.IsTrue(r.IsEmpty); Assert.AreEqual(0, r.Count); Assert.AreEqual(0, r.LongCount); Assert.AreEqual(0, r.Start); }
/// <summary> /// Finds the source row for a given value inside a given row range. /// </summary> /// <param name="srcXCol">Column where the value must be found.</param> /// <param name="xVal">Value to find.</param> /// <param name="range">Range of rows.</param> /// <returns>The row for which the element is equal to the value, or -1 if the value could not be found.</returns> private static int FindSrcXRow(DataColumn srcXCol, AltaxoVariant xVal, ContiguousIntegerRange range) { // Find the src row foreach (int idx in range) { if (srcXCol[idx] == xVal) { return(idx); } } return(-1); }
/// <summary> /// Transpose transpose the table, i.e. exchange columns and rows /// this can only work if all columns in the table are of the same type /// </summary> /// <param name="srcTable">Table to transpose.</param> /// <param name="options">Options that control the transpose process.</param> /// <param name="destTable">Table in which the transposed table should be stored.</param> /// <exception cref="ArgumentNullException"> /// </exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="InvalidOperationException">The data columns to transpose are not of the same type. The first column that has a deviating type is column number + firstDifferentColumnIndex.ToString()</exception> public static void Transpose(this DataTable srcTable, DataTableTransposeOptions options, DataTable destTable) { if (null == srcTable) { throw new ArgumentNullException(nameof(srcTable)); } if (null == destTable) { throw new ArgumentNullException(nameof(destTable)); } if (object.ReferenceEquals(srcTable, destTable)) { throw new ArgumentException(nameof(srcTable) + " and " + nameof(destTable) + " are identical. This inline transpose operation is not supported."); } int numberOfDataColumnsChangeToPropertyColumns = Math.Min(options.DataColumnsMoveToPropertyColumns, srcTable.DataColumnCount); int numberOfPropertyColumnsChangeToDataColumns = Math.Min(options.PropertyColumnsMoveToDataColumns, srcTable.PropertyColumnCount); // number of data columns in the destination table that originates either from converted property columns or from the label column which contains the column names int numberOfPriorDestDataColumns = numberOfPropertyColumnsChangeToDataColumns + (options.StoreDataColumnNamesInFirstDataColumn ? 1 : 0); var dataColumnsToTransposeIndices = ContiguousIntegerRange.FromStartAndEndExclusive(numberOfDataColumnsChangeToPropertyColumns, srcTable.DataColumnCount); if (!AreAllColumnsOfTheSameType(srcTable.DataColumns, dataColumnsToTransposeIndices, out var firstDifferentColumnIndex)) { throw new InvalidOperationException("The data columns to transpose are not of the same type. The first column that has a deviating type is column number " + firstDifferentColumnIndex.ToString()); } using (var suspendToken = destTable.SuspendGetToken()) { destTable.DataColumns.ClearData(); destTable.PropCols.ClearData(); // 0th, store the data column names in the first column if (options.StoreDataColumnNamesInFirstDataColumn) { var destCol = destTable.DataColumns.EnsureExistenceAtPositionStrictly(0, "DataColumnNames", typeof(TextColumn), ColumnKind.Label, 0); for (int j = numberOfDataColumnsChangeToPropertyColumns, k = 0; j < srcTable.DataColumnCount; ++j, ++k) { destCol[k] = srcTable.DataColumns.GetColumnName(j); } } int numberOfExtraPriorDestColumns = (options.StoreDataColumnNamesInFirstDataColumn ? 1 : 0); // 1st, copy the property columns to data columns for (int i = 0; i < numberOfPropertyColumnsChangeToDataColumns; ++i) { var destCol = destTable.DataColumns.EnsureExistenceAtPositionStrictly(i + numberOfExtraPriorDestColumns, srcTable.PropertyColumns.GetColumnName(i), srcTable.PropertyColumns[i].GetType(), srcTable.PropertyColumns.GetColumnKind(i), srcTable.PropertyColumns.GetColumnGroup(i)); var srcCol = srcTable.PropertyColumns[i]; for (int j = numberOfDataColumnsChangeToPropertyColumns, k = 0; j < srcCol.Count; ++j, ++k) { destCol[k] = srcCol[j]; } } // 2rd, transpose the data columns int srcRows = 0; foreach (int i in dataColumnsToTransposeIndices) { srcRows = Math.Max(srcRows, srcTable.DataColumns[i].Count); } // create as many columns in destTable as srcRows and fill them with data Type columnType = dataColumnsToTransposeIndices.Count > 0 ? srcTable.DataColumns[dataColumnsToTransposeIndices[0]].GetType() : null; for (int i = 0; i < srcRows; ++i) { string destColName = string.Format("{0}{1}", options.ColumnNamingPreString, i); if (options.UseFirstDataColumnForColumnNaming) { destColName = string.Format("{0}{1}", options.ColumnNamingPreString, srcTable.DataColumns[0][i]); } var destCol = destTable.DataColumns.EnsureExistenceAtPositionStrictly(numberOfPriorDestDataColumns + i, destColName, false, columnType, ColumnKind.V, 0); int k = 0; foreach (int j in dataColumnsToTransposeIndices) { destCol[k++] = srcTable.DataColumns[j][i]; } } // 3rd, copy the first data columns to property columns for (int i = 0; i < numberOfDataColumnsChangeToPropertyColumns; ++i) { var destCol = destTable.PropertyColumns.EnsureExistenceAtPositionStrictly(i, srcTable.DataColumns.GetColumnName(i), srcTable.DataColumns[i].GetType(), srcTable.DataColumns.GetColumnKind(i), srcTable.DataColumns.GetColumnGroup(i)); var srcCol = srcTable.DataColumns[i]; for (int j = numberOfPriorDestDataColumns, k = 0; k < srcCol.Count; ++j, ++k) { destCol[j] = srcCol[k]; } } // 4th, fill the rest of the property columns with the rest of the data columns for (int i = 0; i < numberOfDataColumnsChangeToPropertyColumns; ++i) { for (int j = 0; j < numberOfPropertyColumnsChangeToDataColumns; ++j) { try { destTable.PropertyColumns[i][j + numberOfExtraPriorDestColumns] = srcTable.PropertyColumns[j][i]; } catch { } } } // and 5th, copy the remaining property columns to property columns for (int i = numberOfPropertyColumnsChangeToDataColumns, j = numberOfDataColumnsChangeToPropertyColumns; i < srcTable.PropertyColumns.ColumnCount; ++i, ++j) { var destCol = destTable.PropertyColumns.EnsureExistenceAtPositionStrictly(j, srcTable.PropertyColumns.GetColumnName(i), false, srcTable.PropertyColumns[i].GetType(), srcTable.PropertyColumns.GetColumnKind(i), srcTable.DataColumns.GetColumnGroup(i)); destCol.Data = srcTable.PropertyColumns[i]; } suspendToken.Resume(); } }
/// <summary> /// Remove the selected columns, rows or property columns. /// </summary> public static void RemoveSelected(IWorksheetController ctrl) { using (var suspendToken = ctrl.DataTable.SuspendGetToken()) { // Property columns are only deleted, if selected alone or in conjunction with data row selection if (ctrl.SelectedPropertyColumns.Count > 0 && ctrl.SelectedPropertyRows.Count == 0 && ctrl.SelectedDataColumns.Count == 0) { ctrl.DataTable.PropCols.RemoveColumns(ctrl.SelectedPropertyColumns); ctrl.SelectedPropertyColumns.Clear(); ctrl.SelectedPropertyRows.Clear(); } // note here: Property rows are only removed indirect by removing data columns // delete the selected columns if there are _only selected columns if (ctrl.SelectedDataColumns.Count > 0 && ctrl.SelectedDataRows.Count == 0) { ctrl.DataTable.RemoveColumns(ctrl.SelectedDataColumns); ctrl.SelectedDataColumns.Clear(); // now the columns are deleted, so they cannot be selected } // if rows are selected, remove them in all selected columns or in all columns (if no column selection= if (ctrl.SelectedDataRows.Count > 0) { ctrl.DataTable.DataColumns.RemoveRowsInColumns( ctrl.SelectedDataColumns.Count > 0 ? (IAscendingIntegerCollection)ctrl.SelectedDataColumns : ContiguousIntegerRange.FromStartAndCount(0, ctrl.DataTable.DataColumns.ColumnCount), ctrl.SelectedDataRows); ctrl.SelectedDataColumns.Clear(); ctrl.SelectedDataRows.Clear(); } // end code for the selected rows suspendToken.Dispose(); } ctrl.TableAreaInvalidate(); // necessary because we changed the selections }
/// <summary> /// Initializes a new instance of the <see cref="DataTableMultipleColumnProxy"/> class. The selected collections determine which columns and rows contribute to this instance. /// The group number is determined by the first selected column (or, if no column is selected, by the first column of the data table). /// </summary> /// <param name="identifier">The identifier of the bundle of columns that are initially set with this constructor.</param> /// <param name="table">The underlying table.</param> /// <param name="selectedDataRows">The selected data rows.</param> /// <param name="selectedDataColumns">The selected data columns.</param> /// <exception cref="System.ArgumentNullException">table must not be null.</exception> public DataTableMultipleColumnProxy(string identifier, DataTable table, IAscendingIntegerCollection selectedDataRows, IAscendingIntegerCollection selectedDataColumns) { if (null == identifier) { throw new ArgumentNullException("identifier"); } if (null == table) { throw new ArgumentNullException("table"); } _dataColumnBundles = new Dictionary <string, ColumnBundleInfo>(); _dataTable = new DataTableProxy(table) { ParentObject = this }; _groupNumber = 0; if (null != selectedDataColumns && selectedDataColumns.Count > 0) { _groupNumber = table.DataColumns.GetColumnGroup(table[selectedDataColumns[0]]); } var bundle = new ColumnBundleInfo(); _dataColumnBundles.Add(identifier, bundle); int maxRowCount = 0; if (selectedDataColumns != null && selectedDataColumns.Count > 0) { for (int i = 0; i < selectedDataColumns.Count; ++i) { var col = table[selectedDataColumns[i]]; if (table.DataColumns.GetColumnGroup(col) == _groupNumber) { InternalAddDataColumnNoClone(bundle, ReadableColumnProxyBase.FromColumn(col)); maxRowCount = Math.Max(maxRowCount, col.Count); } } } else // nothing selected - use all columns of group number 0 { for (int i = 0; i < table.DataColumnCount; ++i) { var col = table[i]; if (table.DataColumns.GetColumnGroup(col) == _groupNumber) { InternalAddDataColumnNoClone(bundle, ReadableColumnProxyBase.FromColumn(col)); maxRowCount = Math.Max(maxRowCount, col.Count); } } } _useAllAvailableDataRows = null == selectedDataRows || selectedDataRows.Count == 0; _participatingDataRows = new AscendingIntegerCollection(_useAllAvailableDataRows ? ContiguousIntegerRange.FromStartAndCount(0, maxRowCount) : selectedDataRows); }
/// <summary> /// Get the next range (i.e. a contiguous range of integers) in descending order. /// </summary> /// <param name="currentposition">The current position into this collection. Use Count-1 for the first time. On return, this is the next position.</param> /// <param name="result">Returns the next contiguous interger range if the return value is <c>true</c>.</param> /// <returns>True if the range data are valid, false if there is no more data. Used as end-of-loop indicator.</returns> /// <remarks>You can use this function in a while loop: /// <code> /// int rangestart, rangecount; /// int currentPosition=selection.Count-1; /// while(selection.GetNextRangeAscending(currentPosition,out rangestart, out rangecount)) /// { /// // do your things here /// } /// </code></remarks> public bool GetNextRangeDescending(ref int currentposition, out ContiguousIntegerRange result) { int rangestart, rangecount; if (currentposition < 0 || currentposition >= Count) { result = ContiguousIntegerRange.Empty; return false; } else { rangestart = this[currentposition]; rangecount = 1; for (currentposition = currentposition - 1; currentposition >= 0; currentposition--) { if (this[currentposition] == (rangestart - 1)) { rangestart--; rangecount++; } else { break; } } result = ContiguousIntegerRange.FromStartAndCount(rangestart, rangecount); return true; } }
/// <summary> /// Get the next range (i.e. a contiguous range of integers) in ascending order. /// </summary> /// <param name="currentposition">The current position into this collection. Use 0 for the first time. On return, this is the next position.</param> /// <param name="result">Returns the next contiguous range if the return value is <c>true</c>.</param> /// <returns>True if the returned data are valid, false if there is no more data.</returns> /// <remarks>You can use this function in a while loop: /// <code> /// int rangestart, rangecount; /// int currentPosition=0; /// while(GetNextRangeAscending(ref currentPosition, out rangestart, out rangecount)) /// { /// // do your things here /// } /// </code></remarks> public bool GetNextRangeAscending(ref int currentposition, out ContiguousIntegerRange result) { int rangestart; int rangecount; if (currentposition < 0 || currentposition >= Count) { result = ContiguousIntegerRange.Empty; return false; } else { rangestart = this[currentposition]; int previous = rangestart; rangecount = 1; for (currentposition = currentposition + 1; currentposition < Count; currentposition++) { if (this[currentposition] == (previous + 1)) { previous++; rangecount++; } else { break; } } result = ContiguousIntegerRange.FromStartAndCount(rangestart, rangecount); return true; } }
/// <summary> /// Finds the source row for a given value inside a given row range. /// </summary> /// <param name="srcXCol">Column where the value must be found.</param> /// <param name="xVal">Value to find.</param> /// <param name="range">Range of rows.</param> /// <returns>The row for which the element is equal to the value, or -1 if the value could not be found.</returns> private static int FindSrcXRow(DataColumn srcXCol, AltaxoVariant xVal, ContiguousIntegerRange range) { // Find the src row foreach (int idx in range) if (srcXCol[idx] == xVal) return idx; return -1; }