How to put into a cell a product of another cell with a variable? - excel

I'm new to vba and I've been trying to make the following code work:
convert = WorksheetFunction.SumIfs(Sheets("Convert").Range("C:C"), _
Sheets("Convert").Range("A:A"), Sheets("Vista").Range("L8"), _
Sheets("Convert").Range("D:D"), Sheets("Vista").Range("C2"), _
Sheets("Convert").Range("E:E"), Sheets("Vista").Range("AC4"))
Sheets("series").Range("L2").FormulaR1C1 = _
"=RC[-8]*"&convert&"
What I'm trying to do, is to put into a variable the result of a SUMIF formula, and use that same value to multiply it with the value of another cell.
It gives me an error of "Application-defined or object-defined error".
Thank you

Arrays Again
The Eliminator
Sub Eliminator()
Dim convert As Long
'Convert = WorksheetFunction.SumIfs(Sheets("Convert").Range("C:C"), _
Sheets("Convert").Range("A:A"), Sheets("Vista").Range("L8"), _
Sheets("Convert").Range("D:D"), Sheets("Vista").Range("C2"), _
Sheets("Convert").Range("E:E"), Sheets("Vista").Range("AC4"))
'e.g.
convert = 1000
Sheets("series").Range("L2").FormulaR1C1 = "=RC[-8]*" & convert
End Sub
Blah, Blah...
Now that we have concluded that the 'Convert' line is causing the error...
Since I use Excel 2003 and you have written the formula correctly, I can only guess that since SumIfs is something like an array formula it can't always be used successfully in VBA, or maybe never!? if you have error values in cells, there might be the solution, because VBA treats them as 'VBA Errors'.
The 'SumIfsless' Solution
So I provided another solution without using SumIfs. You can run it from VBA or any other worksheet. The 'str1' commented lines are for debugging purposes. You can uncomment them and see some 'subtotals' in the Immediate window.
Sub SumIfsArray()
'Variables
'Objects
Dim oRng As Range 'Range of the Sum Column (To Calculate First and Last Row)
'Arrays
Dim arrRngAddress As Variant 'Compare Addresses
Dim arrWs As Variant 'Worksheet Names
Dim arrCol As Variant 'Three Lookup Columns and the Sum Column
Dim arrRng As Variant 'Values of the Compare Addresses
Dim arrRanges As Variant 'The Ranges of the Four Columns
Dim arrArrays As Variant 'The Values of the Four Columns
'Other
Dim iCol As Integer 'Columns Counter
Dim lngFirst As Long 'First Usable Row of Data
Dim lngLast As Long 'Last Usable Row of Data
Dim lngRows As Long 'Number of Rows of Usable Data
Dim lngRow As Long 'Rows Counter
Dim lngSum As Long 'Sum of Values
Dim blnArr As Boolean 'True if all three conditions are met.
' 'Debug Variables
' Const c1 As String = "," 'Debug String Column Separator
' Const r1 As String = vbCr 'Debug String Row Separator
' Dim i1 As Integer 'Debug String Column Counter
' Dim lo1 As Long 'Debug String Rows Counter
' Dim str1 As String 'Debug String Concatenator
'Initialize
arrRngAddress = Array("L8", "C2", "AC4")
arrWs = Array("Convert", "Vista", "series")
arrCol = Array("A:A", "D:D", "E:E", "C:C")
'Program
ReDim arrRng(1 To 3)
With Worksheets(arrWs(1)) 'Worksheet "Vista"
For iCol = 1 To 3
arrRng(iCol) = .Range(arrRngAddress(iCol - 1)).Value
Next
End With
' str1 = "The Values"
' For i1 = 1 To 3: str1 = str1 & r1 & Space(1) & arrRng(i1)
' Next: Debug.Print str1
With Worksheets(arrWs(0)) 'Worksheet "Convert"
'Number of 'usable' rows of data
Set oRng = .Range(arrCol(3))
With oRng
If .Cells(1, 1) <> "" Then
lngFirst = 1
Else
lngFirst = .Cells(1, 1).End(xlDown).Row
End If
lngLast = .Cells(.Rows.Count, .Column).End(xlUp).Row
End With
Set oRng = Nothing
lngRows = lngLast - lngFirst + 1
'Array of Ranges
ReDim arrRanges(1 To 4)
For iCol = 1 To 4
arrRanges(iCol) = Range(Cells(lngFirst, Range(arrCol(iCol - 1)).Column), _
Cells(lngLast, Range(arrCol(iCol - 1)).Column)).Address
Next
' str1 = "The Ranges"
' For i1 = 1 To 4: str1 = str1 & r1 & Space(1) & arrRanges(i1)
' Next: Debug.Print str1
'Array of Arrays
ReDim arrArrays(1 To 4)
For iCol = 1 To 4
arrArrays(iCol) = .Range(arrRanges(iCol)).Value
Next
End With
' str1 = "Values of Ranges" & r1 & Space(1) & "A,D,E,C"
' For lo1 = 1 To lngRows: str1 = str1 & r1 & Space(1): For i1 = 1 To 4
' If i1 <> 1 Then
' str1 = str1 & c1 & arrArrays(i1)(lo1, 1)
' Else: str1 = str1 & arrArrays(i1)(lo1, 1)
' End If: Next: Next: Debug.Print str1
'Sum of Values
For lngRow = 1 To lngRows
For iCol = 1 To 3
If arrArrays(iCol)(lngRow, 1) = arrRng(iCol) Then
blnArr = True
Else
blnArr = False
Exit For
End If
Next
If blnArr = True Then
lngSum = lngSum + arrArrays(4)(lngRow, 1)
End If
Next
' str1 = "The Sum": str1 = str1 & r1 & Space(1) & lngSum
'Output
'Worksheet "series"
Worksheets(arrWs(2)).Range("L2").FormulaR1C1 = "=RC[-8]*" & lngSum
End Sub
P.S. I never ever use variable names with the same name as a worksheet name in the same workbook.

Related

Split words from column and re-join based on criteria from an array

I have a column "D" in my spreadsheet that contains a list of software to install. The list is very long and I only want a few applications to install. Here are a few examples:
Row2: License-E3; Minitab 17; Minitab 18; Proficy Historian 7.0; ;
Row3: License-E3; Attachmate Reflection for UNIX and OpenVMS 14.0; Perceptive Content Desktop Client;
Row4: License-E1; Avaya one-X® Communicator; PipelineBillingInterfaceSystemClient-V2_0; ; SAP-GUI-3Apps; Minitab 18
So, in the first example, I want column D row 2 to just say :
License-E3,Minitab 18
Row 3 to say : License-E3,Reflection
And 4 to say : License-E1,Minitab 18
The rows are auto filtered based on the User Id column, which is Column A in this sheet.
The commented section is basically what I want to do.
Here is my code so far:
Sub FilterSoftware()
Dim cl As Range, rng As Range, Lastrow As Integer, sSoft() As String, i As Long
Dim vSoft As Variant, sNew As String, j As Long, sNewSoft() As String
vSoft = Array("License-E3", "License-E1", "Reflection", "Minitab 18", "RSIGuard", "Java")
Dim Ws As Worksheet: Set Ws = Sheet1
With Ws
Lastrow = .Range("D" & .Rows.Count).End(xlUp).Row
End With
Set rng = Range("D2:D" & Lastrow)
For Each cl In rng.SpecialCells(xlCellTypeVisible)
sSoft = Split(cl, ";")
For i = LBound(sSoft) To UBound(sSoft)
If Not sSoft(i) = " " Then
For j = LBound(vSoft) To UBound(vSoft)
sNewSoft = Split(vSoft(j), " ")
Debug.Print Trim$(sSoft(i))
Debug.Print Trim$(vSoft(j))
'if sSoft(i) contains any words from vSoft(j)
'Join vSoft(j) with comma delimiter until full
'and overwrite in column D
Next j
End If
Next i
Next cl
End Sub
Please, use the next adapted code. It will return in the next column, only for testing reason. If it returns what you need, you can change cl.Offset(0, 1).Value = Join(sNew, ",") with cl.Value = Join(sNew, ","):
Sub FilterSoftware()
Dim cl As Range, rng As Range, Lastrow As Long, sSoft
Dim vSoft, sNew, i As Long, j As Long, t As Long
vSoft = Array("License-E3", "License-E1", "Reflection", "Minitab 18", "RSIGuard", "Java")
Dim Ws As Worksheet: Set Ws = ActiveSheet ' Sheet1
Lastrow = Ws.Range("D" & Ws.rows.count).End(xlUp).row
Set rng = Range("D2:D" & Lastrow)
ReDim sNew(UBound(vSoft)) 'redim the array to a dimension to be sure it will include all occurrences
For Each cl In rng.SpecialCells(xlCellTypeVisible)
sSoft = Split(cl, ";")
For i = LBound(sSoft) To UBound(sSoft)
If Not sSoft(i) = "" Then 'for cases of two consecutive ";"
For j = LBound(vSoft) To UBound(vSoft)
If InStr(1, sSoft(i), vSoft(j), vbTextCompare) > 0 Then
sNew(t) = vSoft(j): t = t + 1: Exit For
End If
Next j
End If
Next i
If t > 0 Then
ReDim Preserve sNew(t - 1) 'keep only the array filled elements
cl.Offset(0, 1).Value = Join(sNew, ",") 'put the value in the next column (for testing reason)
ReDim sNew(UBound(vSoft)): t = 0 'reinitialize the variables
End If
Next cl
End Sub

Excel VBA: What is the best way to sum a column in a dataset with variable amounts of lines?

I need to sum two columns (B and C) in a dataset. The number of rows with data will vary between 1 and 17. I need to add the sums two rows beneath the last row of data (end result example in image 1).
My code worked beautifully for one dataset, but I am getting an error
Run-time error'6': Overflow
for a different dataset. What am I doing wrong?
'Units total
Windows("Final_Files.xlsb").Activate
Sheets("Revenue Summary").Select
lastrow = Worksheets("Revenue Summary").Cells(Rows.Count, 2).End(xlUp).Row
Dim a As Integer
a = 10000
For i = lastrow To 2 Step by - 1
a = a + Worksheets("Revenue Summary").Cells(i, 2).Value
Next
Worksheets("Revenue Summary").Cells(lastrow + 2, 2).Value = a
Correct End Result
You can try below sub-
Sub SumBC()
Dim sh As Worksheet
Dim lRowB As Long, lRowC As Long
Dim bSum As Double, cSum As Double
Windows("Final_Files.xlsb").Activate
Set sh = Worksheets("Revenue Summary")
lRowB = sh.Cells(sh.Rows.Count, 2).End(xlUp).Row
lRowC = sh.Cells(sh.Rows.Count, 3).End(xlUp).Row
bSum = WorksheetFunction.Sum(sh.Range("B2:B" & lRowB))
cSum = WorksheetFunction.Sum(sh.Range("C2:C" & lRowC))
sh.Cells(lRowB + 2, 2) = bSum
sh.Cells(lRowC + 2, 3) = cSum
sh.Activate
Set sh = Nothing
End Sub
Remember: If you want to run same sub multiple time then you need clear totals otherwise it will add totals again again below of last totals.
Your code is perfect but there is only one error. You have initialized variable 'a' with 10000. Change it to 0.
a = 0
then your code will be perfect.
Add Totals to Multiple Columns
If you're not OP: It is easy to test the code. Open a new workbook and insert a module. Copy the code into the module. Uncomment the Sheet1 line, and outcomment the Revenue Summary line. In worksheet Sheet1 add some numbers in columns 2 and 3 and your ready.
Run only the insertTotals procedure. The calculateSumOfRange is called when needed.
Play with the constants in insertTotals and change the values in the columns. Add text, error values, booleans to see how the code doesn't break.
The issue with Application.Sum or WorksheetFunction.Sum is that it fails when there are error values in the range. That's what the calculateSumOfRange is preventing. If there is an error value, the loop approach is used. If not, then Application.Sum is the result.
You can use the calculateSumOfRange in Excel as a UDF. Just don't include the cell where the formula is and you're OK, e.g. =calculateSumOfRange(A1:B10).
The Code
Option Explicit
Sub insertTotals()
Const FirstRow As Long = 2 ' First Row of Data
Const LastRowCol As Long = 2 ' The column where the Last Row is calculated.
Const TotalsOffset As Long = 2 ' 2 means: 'data - one empty row - totals'
Dim Cols As Variant
Cols = Array(2, 3) ' add more
'With ThisWorkbook.Worksheets("Sheet1")
With Workbooks("Final_Files.xlsb").Worksheets("Revenue Summary")
' Define Last Row ('LastRow') in Last Row Column ('LastRowCol').
Dim LastRow As Long
LastRow = .Cells(.Rows.Count, LastRowCol).End(xlUp).Row
' Define Last Row Column Range ('rng').
Dim rng As Range
Set rng = .Range(.Cells(FirstRow, LastRowCol), _
.Cells(LastRow, LastRowCol))
Dim j As Long
' Validate Columns Array ('Cols').
If LBound(Cols) <= UBound(Cols) Then
' Iterate columns in Columns Array.
For j = LBound(Cols) To UBound(Cols)
' Use 'Offset' to define the current Column Range and write
' its calculated total below it.
.Cells(LastRow + TotalsOffset, Cols(j)).Value = _
calculateSumOfRange(rng.Offset(, Cols(j) - LastRowCol))
Next j
End If
End With
End Sub
Function calculateSumOfRange(SourceRange As Range) _
As Double
' Initialize error handling.
Const ProcName As String = "calculateSumOfRange"
On Error GoTo clearError ' Turn on error trapping.
' Validate Source Range.
If SourceRange Is Nothing Then
GoTo NoRange
End If
' Calculate Sum of Range.
Dim CurrentValue As Variant
CurrentValue = Application.Sum(SourceRange)
Dim Result As Double
If Not IsError(CurrentValue) Then
Result = CurrentValue
Else
Dim Data As Variant
If SourceRange.Rows.Count > 1 Or SourceRange.Columns.Count > 1 Then
Data = SourceRange.Value
Else
ReDim Data(1, 1)
Data(1, 1) = SourceRange.Value
End If
Dim i As Long
Dim j As Long
For i = 1 To UBound(Data, 1)
For j = 1 To UBound(Data, 2)
CurrentValue = Data(i, j)
If IsNumeric(CurrentValue) And _
Not VarType(CurrentValue) = vbBoolean Then
Result = Result + CurrentValue
End If
Next j
Next i
End If
' Write result and exit.
calculateSumOfRange = Result
GoTo ProcExit
' Labels
NoRange:
Debug.Print "'" & ProcName & "': No range (Nothing)."
GoTo ProcExit
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
ProcExit:
End Function
The following code summs up all the rows under "B2" and "C2". Adapt it to your needs.
' Keep a reference to the worksheet
Dim ws as Worksheet
Set ws = Worksheets("Revenue Summary")
' This is how many rows there are.
Dim rowCount as Long
rowCount = ws.Cells(ws.Rows.Count, 2).End(xlUp).Row-1
' This is the summation operation over each column
Dim b as Double, c as Double
b = WorksheerFunction.Sum(ws.Range("B2").Resize(rowCount,1))
c = WorksheerFunction.Sum(ws.Range("C2").Resize(rowCount,1))
' This writes the sum two cells under the last row.
ws.Range("B2").Cells(rowCount+2,1).Value = b
ws.Range("C2").Cells(rowCount+2,1).Value = c

How to use each value in column 1 to add comment (NOTE) from 2 different columns?

I need a dynamic way to add Note in which cell in my ID column A. However the comments need to use the information from Column B and C. ex: ON 01/13/2020, Anne.
I am not sure how to check how many times each value from column A will appear and use information from column D and B to create the comment (NOTE)..
result I need. All the time the ID number will be the same the comments need to be the same as well.
The code I am using is
Sub Cmt_test()
Sheet1.Range("A2").AddComment "On " & Sheet1.Range("D2") & ", " & Sheet1.Range("B2")
End Sub
I don't know how I can make it dynamic to get the information all the time the same ID appears. Maybe if I use Loop on column A would it be possible that all the time the loop finds the same ID to add the comment using the information from column D and B?
Write Comments to Each Cell in a Column
Option Explicit
Sub addComments()
Const wsName As String = "Sheet1"
Const FirstRow As Long = 2
Const LastRowCol As Long = 1 ' or "A"
Const str1 As String = "On "
Const str2 As String = ", "
Dim Cols As Variant: Cols = Array(1, 2, 4)
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim LastRow: LastRow = ws.Cells(ws.Rows.Count, LastRowCol).End(xlUp).Row
Dim Vals As Variant: ReDim Vals(UBound(Cols))
' Define Source Range.
Dim rng As Range: Set rng = ws.Range(ws.Cells(FirstRow, Cols(0)), _
ws.Cells(LastRow, Cols(0)))
' Write Column Ranges to Arrays.
Dim j As Long
For j = 0 To UBound(Cols)
Vals(j) = rng.Offset(, Cols(j) - Cols(0))
Next j
' Loop through elements (rows) of Source Array
' and write comments to a dictionary.
Dim dict As Object, Curr As Variant, i As Long
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(Vals(0))
Curr = Vals(0)(i, 1)
If dict(Curr) <> "" Then
dict(Curr) = dict(Curr) & vbLf & str1 _
& Format(Vals(2)(i, 1), "mm/dd/yyyy") & str2 & Vals(1)(i, 1)
Else
dict(Curr) = str1 _
& Format(Vals(2)(i, 1), "mm/dd/yyyy") & str2 & Vals(1)(i, 1)
End If
Next i
' Write comments from the dictionary to Source Range.
rng.ClearComments
Dim cel As Range
For Each cel In rng.Cells
cel.AddComment dict(cel.Value)
Next cel
End Sub

Is there ability to split cells while retaining the values of adjacent columns?

The IDs column in the first table contains multiple values in each cell that needs to be split. However, the unique issue is to retain both [name] and [description] info by ID into a new table.
.
The following VBA code performs the transpose paste option. This is what I am starting with to split cells with Chr(10), or new line as the delimiter:
Sub splitText()
'splits Text active cell using ALT+10 char as separator
Dim splitVals As Variant
Dim totalVals As Long
splitVals = Split(ActiveCell.Value, Chr(10))
totalVals = UBound(splitVals)
Range(Cells(ActiveCell.Row, ActiveCell.Column + 1), Cells(ActiveCell.Row, ActiveCell.Column + 1 + totalVals)).Value = splitVals
End Sub
Other than this, I am still searching for ideas.
Maybe this will help:
Sub splitText()
'splits Text active cell using ALT+10 char as separator
Dim splitVals As Variant
Dim lngRow As Long, lngEl As Long
With Sheet2
'Range A2:A5
For lngRow = 5 To 2 Step -1
splitVals = Split(.Range("A" & lngRow).Value, Chr(10))
'the first value
.Range("A" & lngRow).Value = splitVals(0)
'remaining values
For lngEl = 1 To UBound(splitVals)
.Rows(lngRow + lngEl).Insert
.Range("A" & lngRow + lngEl).Value = splitVals(lngEl)
.Range("B" & lngRow + lngEl & ":C" & lngRow + lngEl).Value = .Range("B" & lngRow & ":C" & lngRow).Value
Next lngEl
Next lngRow
End With
End Sub
Change Sheet Code/Name and Range as necessary.
Before:
After:
It's a bit more involved than your solution because you have to insert the correct number of rows below the targeted cell and then copy the IDs and the other data into the new rows. Here's an example to help you along.
There's a little "trickery" I'm using when I calculate the offset value. I'm doing this because you can assume that all arrays from the Split function will begin indexing at 0, but my personal habit is to write code that can work with either a 0 or 1 lower bound. Calculating and using an offset makes it all work for the loops and indexes.
Option Explicit
Sub test()
SplitText ActiveCell
End Sub
Sub SplitText(ByRef idCell As Range)
Dim splitVals As Variant
Dim totalVals As Long
splitVals = Split(idCell.Value, Chr(10))
If LBound(splitVals) = -1 Then
'--- the split character wasn't found, so exit
Exit Sub
End If
Dim offset As Long
offset = IIf(LBound(splitVals) = 0, 1, 0)
totalVals = UBound(splitVals) + offset
Dim idSheet As Worksheet
Set idSheet = idCell.Parent
Dim idRow As Long
idRow = idCell.Row
'--- insert the number of rows BELOW the idCell to hold all
' the split values
Dim i As Long
For i = 1 To totalVals - 1
idSheet.Rows(idRow + 1).Insert
Next i
'--- now add the IDs to all the rows and copy the other columns down
Const TOTAL_COLUMNS As Long = 3
Dim j As Long
Dim startIndex As Long
startIndex = LBound(splitVals) + offset
For i = startIndex To totalVals
idCell.Cells(i, 1) = splitVals(i - offset)
For j = 2 To TOTAL_COLUMNS
idCell.Cells(i, j) = idCell.Cells(1, j)
Next j
Next i
End Sub

Excel VBA parse column, extract all substrings

I'm trying to parse a column that contains data in the following format in each cell -
pull: test1
or
pull: test2|pull: test3|.....
or
other: blah...
I only want a grab each "Pull: test" and place 1 in each row in a new worksheet like below, and ignore any parts of the cell that don't begin with "pull: " -
pull: test1
pull: test2
pull: test3
...
What I have so far just pulls the entire column and pastes into the same spreadsheet, I'm not sure how to separate the items in each cell into their own rows. I also can't get it to pull to a different worksheet correctly either (commented out my attempt)
Sub InStrDemo()
Dim lastrow As Long
Dim i As Integer, icount As Integer
'Sheets.Add.Name = "TEST"
lastrow = ActiveSheet.Range("A10000").End(xlUp).Row
For i = 1 To lastrow
If InStr(1, LCase(Range("E" & i)), "pull:") <> 0 Then
icount = icount + 1
'Sheets("TEST").Range("A" & icount & ":E" & icount) = Worksheets("SearchResults").Range("A" & i & ":E" & i).Value
Range("L" & icount) = Range("E" & i).Value
End If
Next i
End Sub
Untested, written on mobile.
Option Explicit
Sub testDemo()
Dim sourceSheet as worksheet
Set sourceSheet = ActiveSheet ' would be more reliable to qualify the workbook and worksheet by name'
Dim outputSheet as worksheet
Set outputSheet = thisworkbook.worksheets.add
Dim lastRow As Long
lastrow = sourceSheet.Range("A10000").End(xlUp).Row
' I assume column E needs to be parsed'
Dim arrayOfValues() as variant
arrayOfValues = sourceSheet.range("E1:E" & lastRow)
Dim rowIndex as long
Dim columnIndex as long
Dim splitString() as string
Dim cumulativeOffset as long
Dim toJoin(0 to 1) as string
toJoin(0) = "pull: test" ' Might speed up string concatenation below'
Dim outputArray() as string
With outputsheet.range("A1") ' The first row you want to start stacking from'
For rowIndex = 1 to lastRow
' Single dimensional, 0-based array'
splitString = VBA.strings.split(vba.strings.lcase$(arrayOfValues(rowIndex,1)), "pull: test",-1, vbbinarycompare)
Redim outputArray(1 to (ubound(splitString)+1), 1 to 1)
For columnIndex = lbound(splitString) to ubound(splitString)
toJoin(1) = splitString(columnIndex)
Outputarray(columnIndex+1,1) = VBA.strings.join(toJoin, vbnullstring)
Next columnIndex
'Instead of splitting upon a delimiter, then prepending the delimiter to each array element (as is done above), you could repeatedly call instr(), use mid$() to extract the sub-string, then increase the argument passed to the "Start" parameter in instr() (effectively moving from start to end of the string) -- until instr() returns 0. Then move on to the next string in the outer loop.'
.offset(cumulativeOffset,0).resize(Ubound(outputArray, 1), 1).value2 = outputArray
cumulativeOffset = cumulativeOffset + ubound(splitString)
Next rowIndex
End Sub

Resources