How to loop on range of rows in LibreOffice Calc spreadsheet, comparing cell values, setting cell values and deleting row, if condition is true - spreadsheet

I have the following requirement in my LibreOffice Calc spreadsheet:
ForEach Row 'r' in selected range,
starting from the last row in the range,
and moving backwards (up) one row at a time,
do some cell value comparisons, and based on that, either skip the row,
or set some cell values, and delete the selected row,
then proceed with the same process, with the row just above that.
ie.,
Representing CellValue(Column[A], Row[r])
as A[r],
And Representing the row before (just above) that,
as A[r-1],
I need to do the following:
FOR (r = LastRowInSelectedRange; r>1; r=r-1) {
IF FollowingConditionsAreTrue (
(r > 1)
AND (A[r] IsEqualTo A[r-1])
AND (B[r] IsEqualTo C[r-1])
AND (E[r] IsEqualTo E[r-1])
) ThenDoTheFollowing {
SET C[r-1] = C[r]
DeleteRow(r)
} EndIF
} EndFOR
Question:
How can we implement this in LibreOffice Calc?

The following should do the trick. It assumes your data are in the range A1:E10 in a sheet called Sheet1. To use a different range change the appropriate lines. If you want to select the range by hand, comment out the line oCellRange = oSheet.getCellRangeByName("A1:E10") and uncomment the line oCellRange = oDoc.getCurrentSelection(). That will only work if you have a single selection, not multiple ones.
Sub iterateThroughRange()
Dim oDoc As Object
oDoc = ThisComponent
Dim oSheet As Object
oSheet = oDoc.getSheets().getByName("Sheet1")
Dim oCellRange As Object
oCellRange = oSheet.getCellRangeByName("A1:E10")
'The above line gets a named range. To get a slected area instead
'comment it out and uncomment the following line:
'oCellRange = oDoc.getCurrentSelection()
Dim oTableRows As Object
oTableRows = oCellRange.getRows()
Dim nRows As Integer
nRows = oTableRows.getCount()
Dim oTableColumns As Object
oTableColumns = oCellRange.getColumns()
Dim nColumns As Integer
nColumns = oTableColumns.getCount()
Dim oThisRow As Object, oPreviousRow As Object
Dim oThisRowData() As Variant, oPreviousRowData() As Variant
Dim oThisRowAddress As Variant
For r = nRows - 1 To 1 Step - 1
oThisRow = oCellRange.getCellRangeByPosition(0, r, nColumns - 1, r)
oThisRowData = oThisRow.getDataArray()
oPreviousRow = oCellRange.getCellRangeByPosition(0, r - 1, nColumns - 1, r - 1)
oPreviousRowData = oPreviousRow.getDataArray()
'Column A = index 0
'Column B = index 1
'Column C = index 2
'Column E = index 4
If oThisRowData(0)(0) = oPreviousRowData(0)(0) AND _
oThisRowData(0)(1) = oPreviousRowData(0)(2) AND _
oThisRowData(0)(4) = oPreviousRowData(0)(4) Then
oPreviousRowData(0)(2) = oThisRowData(0)(2)
oPreviousRow.setDataArray(oPreviousRowData)
'The following two lines delete the range and move the cells up
oThisRowAddress = oThisRow.getRangeAddress()
oSheet.removeRange(oThisRowAddress, com.sun.star.sheet.CellDeleteMode.UP)
'To delete the entire row instead of just the affected cells,
'comment out the above two lines and uncomment the following line:
'oTableRows.removeByIndex(r, 1)
End If
Next r
End Sub

Related

Pasting inside a range, which is counting up the cells for its second parameter, the row

I would like to paste data from a range, copy it in a new range.
This new range is supposed to count up its row by +1 (represented by variable i)
for each value (self) that is being pasted.
I use self, because i need to extract values from a range that includes empty cells.
I tested the first part, the debug print works, so the self values are obtained correctly.
I must be making a mistake with the Range(27, i) part, and also with the Set i = 15 part.
Help very appreciated.
sincerely
Private Sub EvaluateButton_Click()
Dim EvaRange As Range 'Evaluation range
Dim i As Integer 'Counter for free columns
Worksheets("testsheet").Activate
Set EvaRange = Range("C10:C999")
EvaRange.Copy Range("Z15")
Range("Z15:Z999").Select
Set i = 15
For Each self In Selection.SpecialCells(xlCellTypeConstants)
Debug.Print (self)
i = i + 1
self.Copy Range(27, i)
Next
End Sub
Edit: i tried a second version, i think that variables are defined like this:
Dim x As Integer
x = 6
So shouldn't this work?
Private Sub EvaluateButton_Click()
Dim EvaRange As Range
Dim i As Integer
Worksheets("testsheet").Activate
Set EvaRange = Range("C10:C999")
EvaRange.Copy Range("Z15")
Range("Z15:Z999").Select
i = 15
For Each self In Selection.SpecialCells(xlCellTypeConstants)
Debug.Print (self)
self.Copy Range(27, i)
i = i + 1
Next
This will remove my empty lines :)
Now, I have the problem, that my last value is duplicated down the line, a new challenge to handle :)
Private Sub EvaluateButton_Click()
Dim EvaRange As Range 'Evaluation range
Dim i As Integer 'Counter for free columns
Worksheets("testsheet").Activate
Set EvaRange = Range("C10:C999")
EvaRange.Copy Range("Z15")
Range("Z15:Z999").Select
i = 15
For Each self In Selection.SpecialCells(xlCellTypeConstants)
Debug.Print (self)
self.Copy Cells(i, 27)
i = i + 1
Next

Using a variable within a formula

I'm trying to use a variable within a formula but the result I am getting is FALSE, when it should be returning a Lookup value, starting from the bottom of a table and going up.
Private Sub NewTaxCode()
Dim TaxCode2 As Long
Dim B As Range
Dim TaxCode3 As Range
Dim TaxCode4 As Range
Worksheets("P11Combined").Activate
Set B = Range("A:A")
Worksheets("E'ee Details").Range("U1").Value = "=Match(RC[-4],P11Combined!C[-18],0)"
TaxCode2 = Worksheets("E'ee Details").Range("U1").Value
Set TaxCode3 = Range("A:A").Find(What:="TAX:", After:=B(TaxCode2))
Set TaxCode4 = Range(TaxCode3.Address).End(xlDown).Offset(1, 2)
TaxCode5 = Range(TaxCode4.Address).FormulaR1C1 = "=LOOKUP(2,1/(R[-12]C:R[-1]C<>"" ""),(R[-12]C[-1]:R[-1]C[-1]))"
The last line is where I am having the issues. The formula works fine on a spreadsheet but I can't get it to work in VBA.
The last line of your code is of the format
variable = logical_expression
and is setting a variable (TaxCode5) to be either True or False depending on whether or not the formula in the cell referred to by TaxCode4 is the same as the one in the code.
It does not modify the formula in the cell referred to by TaxCode4.
It appears you want:
Private Sub NewTaxCode()
Dim TaxCode2 As Long
Dim B As Range
Dim TaxCode3 As Range
Dim TaxCode4 As Range
Dim TaxCode5 As Variant
Set B = Worksheets("P11Combined").Range("A:A")
Worksheets("E'ee Details").Range("U1").FormulaR1C1 = "=Match(RC[-4],P11Combined!C[-18],0)"
TaxCode2 = Worksheets("E'ee Details").Range("U1").Value
Set TaxCode3 = B.Find(What:="TAX:", After:=B(TaxCode2))
Set TaxCode4 = TaxCode3.End(xlDown).Offset(1, 2)
TaxCode4.FormulaR1C1 = "=LOOKUP(2,1/(R[-12]C:R[-1]C<>"" ""),(R[-12]C[-1]:R[-1]C[-1]))"
TaxCode5 = TaxCode4.Value
'...
End Sub

Copy and paste information based on matching IDs where one sheet has rows in the pivot table

I have a code that allows me to copy and paste thousands of rows of information based on matching IDs. However the code does not seem to run well in a pivot table. In sheet 4, the IDs are put into a pivot table while in sheet 1 the IDs and the information are not in pivot table (Both IDs in sheet 4 and 1 are in the same column which is column A). However, the IDs appeared more than once in sheet 1. Thus, when i try to run the code, it gave an error that said Cannot enter a null value as an item or field name in pivot table report" on the line 'rngTracker.Value = arrT found below.
Sub Sample()
Dim rngTracker As Range
Dim rngMaster As Range
Dim arrT, arrM
Dim dict As Object, r As Long, tmp
With Workbooks("FAST_Aug2015_Segment_Out_V1.xlsm")
Set rngTracker = .Sheets("Sheet4").Range("A5:D43000")
Set rngMaster = .Sheets("Sheet1").Range("A2:C200000")
End With
'get values in arrays
arrT = rngTracker.Value
arrM = rngMaster.Value
'load the dictionary
Set dict = CreateObject("scripting.dictionary")
For r = 1 To UBound(arrT, 1)
dict(arrT(r, 1)) = r
Next r
'map between the two arrays using the dictionary
For r = 1 To UBound(arrM, 1)
tmp = arrM(r, 1)
If dict.exists(tmp) Then
arrT(dict(tmp), 4) = arrM(r, 3)
End If
Next r
rngTracker.Value = arrT 'Error shown on this line'
End Sub
Above is the code that i have and gave error as mention above. Would appreciate any help. Thank you. :) Below is the image of the pivot table in sheet 4. The column header called "Acc Seg" is not part of the pivot table but it is where the data will be paste from sheet 1 when both IDs in sheet 4 and sheet 1 matched.
Option Explicit
Public Sub Sample()
Const T As Long = 43000
Const M As Long = 200000
Dim arrT1 As Variant, arrM1 As Variant, rngT2 As Range
Dim arrT2 As Variant, arrM2 As Variant, dict As Object, r As Long
With Workbooks("TEST2.xlsm") 'get values in arrays
Set rngT2 = .Sheets("Sheet4").Range("D5:D" & T)
arrM1 = .Sheets("Sheet1").Range("A2:A" & M)
arrM2 = .Sheets("Sheet1").Range("C2:C" & M)
arrT1 = .Sheets("Sheet4").Range("A5:A" & T)
arrT2 = rngT2
End With
Set dict = CreateObject("Scripting.Dictionary")
For r = 1 To UBound(arrT1) 'load the dictionary
dict(arrT1(r, 1)) = r
Next r
For r = 1 To UBound(arrM1, 1) 'map between the arrays using the dictionary
If dict.exists(arrM1(r, 1)) Then arrT2(dict(arrM1(r, 1)), 1) = arrM2(r, 1)
Next r
rngT2 = arrT2
End Sub

Include empty spaces when selecting from row in Open XML

My Excel looks like this
A B C D
1 2 3
I use this,
Dim row As DocumentFormat.OpenXml.Spreadsheet.Row = sheetData.Descendants(Of DocumentFormat.OpenXml.Spreadsheet.Row)().FirstOrDefault(Function(y) y.RowIndex.Value = 1)
I only get 3 Cells (B,C,D) in my result. How do i include the blank spaces?
Excel file contains only cells filled with their addresses. Empty cells are "virtual".
You can check by address cells, the "missing" cells are .
To translate the address (which is in "A1" style) to number index, you can use this function (credit: codeproject Article: Read and Write Microsoft Excel with Open XML SDK):
dim regexColName = New Regex("[A-Za-z]+", RegexOptions.Compiled)
Private Function ConvertCellReferenceToNumber(cellReference As String) As Integer
Dim colLetters = regexColName.Match(cellReference).Value.ToCharArray()
Array.Reverse(colLetters)
Dim convertedValue = Asc(colLetters(0)) - 65
For i = 1 To colLetters.Length - 1
Dim current = Asc(colLetters(i)) - 64
convertedValue += current * Math.Pow(26, i)
Next
Return convertedValue
End Function
with this function you can simulate empty cells:
Dim row As Row = SheetData.Descendants(Of Row)().FirstOrDefault(Function(y) y.RowIndex.Value = 2)
Dim cells = row.Descendants(Of Cell).ToDictionary(
Function(cell) ConvertCellReferenceToNumber(cell.CellReference),
function(cell) cell)
For i = 0 To cells.Keys.Max()
Dim c As Cell
If (cells.TryGetValue(i, c)) Then
Console.WriteLine(c.CellValue) 'need hanle for special values
Else
Console.WriteLine("empty")
End If
Next

Randomly select an item from a list based on a class, repeat number of times based on different numbers

I am not familiar with using macro's, but I think that what I would like excel to perform is best handled with a macro. So I can use all the input you may have!
I have these headers;
ID Tag Pen Sex Weight Class Inside range
With 450 rows of data. Based on the distribution of the weight data, I have in two other columns (class and number) the number of rows I want to select within each class. The selected rows must have the value "Yes" in the column "Inside range".
I want to randomly select the rows, based on the number needed for each class, and copy these rows to a new sheet. It sums up to 30 rows in the new sheet.
I hope you have a suggestion how to complete this action!
can you try the following, you will need to add a reference to Microsoft Scripting Runtime library:
Const rowCount = 450
Public Sub copyRows()
Dim i As Integer
Dim j As Integer
Dim classes As Scripting.Dictionary
Dim source As Worksheet
Dim colNumber As Integer
Dim colClassName as Integer
Dim colInsideRange As Integer
Dim allSelected As Boolean
Dim randomRow as Integer
Dim sumRemaining as Integer
allSelected = False
Set source = Worksheets("YourWorksheetName")
colClassName = 6 'this is the column number where class names are entered. I am assuming 6
colNumber = 7 'this is the column number where number of rows to be selected are entered. I am assuming 7
colInsideRange = 8 'this is the column number where "Inside Range" values are entered. I am assuming 9
For i = 2 to rowCount + 1 'assuming you have a header row
classes(CStr(source.Cells(i, colClassName))) = CInt(source.cells(i, colNumber)
Next i
Do until allSelected
Randomize
randomRow = Int ((Rnd * 450) + 2) 'assuming you have a header row, + 1 if you don't
If classes(CStr(source.Cells(randomRow, colClassName))) = 0 Then
With classes
sumRemaining = 0
For j = 1 to .Count - 1
sumRemaining = sumRemaining + .Items(j)
If sumRemaining > 0 Then Exit For
Next j
allSelected = (sumRemaining = 0)
End With
Else
source.Cells(randomRow, colInsideRange) = "Yes"
classes(CStr(source.Cells(randomRow, colClassName))) = classes(CStr(source.Cells(randomRow, colClassName))) - 1
End If
Loop
'Enter your code to copy rows with "Inside Range" = "Yes"
End Sub
Sorry if there are some errors or typos, I wrote from my mobile phone.