I want to create a summary table in a new sheet, and at the moment I'm just doing it very crudely. I will try a more elegant solution in the future.
Anyway, this is the code I have so far:
Sub createsummarytable()
Worksheets.Add().Name = "datasummary"
With Worksheets("datasummary")
Dim i As Long
Dim Startpoint As Long
Startpoint = -5
For i = 1 To 40
.Cells(Startpoint + (5 * i), 1).Value = "Block" & "i"
Next i
End With
End Sub
I am getting the error in the title on line: .Cells(Startpoint + (5 * i), 1).Value = "Block" & "i"
If anyone wants to make the code more elegant in addition to solving the error, that would be appreciated.
Off-by-one. There is no column/row 0 in Excel; -5 + (5 * 1) evaluates to 0:
.Cells(0, 1).Value = 42 'same error
You need to adjust by +1:
For i = 1 To 40
.Cells(Startpoint + (5 * i) + 1, 1).Value = "Block" & i
Next i
If your code works as intended, describe your working code in a new question on Code Review.
#Mat's Mug picked up on the off-by-one error, and #kpg987 got your use of "i" as a string-literal instead of a variable, but your code can be improved.
Here are some of the changes I made:
Scope the procedure to Private or Public
Use meaningful names
Use Constants to determine the starting row and number of each rows for each block. StartingPoint = -5 isn't useful, whereas START_ROW = 1 is quite clear about what it is and where it will actually start.
Adjust the formula to use the constants.
Refer to the target workbook explicitly, otherwise the active workbook will be used.
Set a reference to the added worksheet when using the add command.
Use the strongly typed reference with the With command, because With Worksheets("datasummary") will be late bound)
Use camelCasing for variable names
Result:
Private Sub createSummaryTable()
Const WORKSHEET_NAME As String = "datasummary"
Const START_ROW As Long = 1
Const BLOCK_COLUMN as Long = 1
Const BLOCK_SIZE As Long = 5
Const BLOCK_COUNT As Long = 40
Const BLOCK_PREFIX As String = "Block"
Dim dataSummary As Worksheet
Set dataSummary = ThisWorkbook.Worksheets.Add
With dataSummary
.Name = WORKSHEET_NAME
Dim blockCounter As Long
For blockCounter = 1 To BLOCK_COUNT
.Cells(START_ROW + (BLOCK_SIZE * (blockCounter - 1)), BLOCK_COLUMN).Value = BLOCK_PREFIX & blockCounter
Next blockCounter
End With
End Sub
a "no-loop" approach:
Private Sub createSummaryTable2()
Dim dataSummary As Worksheet
With ThisWorkbook.Worksheets.Add
.Name = "datasummary"
With .Range("A1").Resize((40 - 1) * 5 + 1)
.FormulaR1C1 = "=IF(MOD(ROW(),5)=1,""Block"" & int(ROW()/5)+1,"""")"
.Value = .Value
End With
End With
End Sub
Related
I want to automate a calculation for a measurement data Excel file.
While I made the function work, I could only figure out how to do that for one row at a time. The calculation is specific to the data on every row.
I thought I could change the Range("J3") value to Range("J3:J52") for a capacity of 50 calculations.
How do I make the function calculate for every row, separately, using the data of said specific row?
It doesn't matter if it runs for all 50 rows or if I have to figure out some loop function to find how many rows to calculate for, as long as one button press in the end will make the magic happen.
I have included a screenshot of the sheet for reference, the main calculation is done in Excel, but what this is doing is choosing the correct option out of a few different correction calculations and explaining to the user why.
I think there are some unnecessary Dim lines at the start but if it runs, I wasn't going to remove them.
'The main function, activated by a simple button Sub'
Function ISO16032()
'DeltaL Range'
Dim DeltaL As Range
Set DeltaL = Range("F3")
'Result is the corrected value in G column'
Dim Result As Long
'Note is the calc note in H column'
Dim Note As String
'X is the DeltaL between noise and background noise'
Dim x As Long
x = Range("F3").Value
Select Case Range("F3").Value
'No correction when X = > 10'
Case 10.6 To 200
Result = Range("J3")
Range("G3").Value = Result
Note = "No correction"
Range("H3").Value = Note
'Correction according to ISO16032 when X = between 4 and 10'
Case 3.6 To 10.5
Result = Range("K3")
Range("G3").Value = Result
Note = "Correction per ISO16032"
Range("H3").Value = Note
'Maximal correction value set to 2,2 dB if X < 4'
Case 0.1 To 3.5
Result = Range("L3")
Range("G3").Value = Result
Note = "Correction limit set to 2,2 dB"
Range("H3").Value = Note
'If x = < 0, the measurement is invalid'
Case Else
Note = "Repeat measurement!"
Range("H3").Value = Note
End Select
End Function
Hi and welcome to stackoverflow, I think that a simple loop, with the addition of an argument to your ISO function allows you to solve your problem like this
Sub Looping()
For i = 3 To 52
' Convert i to String because we need to concatenate with the letter F, G, H...
Call ISO16032(CStr(i))
Next
End Sub
Function ISO16032(Cell_X)
'DeltaL Range'
Dim DeltaL As Range
Set DeltaL = Range("F" + Cell_X)
'Result is the corrected value in G column'
Dim Result As Long
'Note is the calc note in H column'
Dim Note As String
'X is the DeltaL between noise and background noise'
Dim x As Long
x = Range("F" + Cell_X).Value
Select Case Range("F" + Cell_X).Value
'No correction when X = > 10'
Case 10.6 To 200
Result = Range("J" + Cell_X)
Range("G" + Cell_X).Value = Result
Note = "No correction"
Range("H" + Cell_X).Value = Note
'Correction according to ISO16032 when X = between 4 and 10'
Case 3.6 To 10.5
Result = Range("K" + Cell_X)
Range("G" + Cell_X).Value = Result
Note = "Correction per ISO16032"
Range("H" + Cell_X).Value = Note
'Maximal correction value set to 2,2 dB if X < 4'
Case 0.1 To 3.5
Result = Range("L" + Cell_X)
Range("G" + Cell_X).Value = Result
Note = "Correction limit set to 2,2 dB"
Range("H" + Cell_X).Value = Note
'If x = < 0, the measurement is invalid'
Case Else
Note = "Repeat measurement!"
Range("H" + Cell_X).Value = Note
End Select
End Function
EDIT: think indented your code like I did to make it more readable too (or like #Darren Bartrup-Cook did when editing your question)
This is more of an example of how to calculate each row.
Just getting your code to work across multiple rows.
Cells
With...End With Statement
'No need for Sub to call function that doesn't return anything.... just write a sub.
Public Sub ISO16032()
Dim LastRow As Long
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, 10).End(xlUp).Row
Dim RowCounter As Long
Dim Result As Long
Dim Note As String
'Only calculate if there is data in rows 3 onwards.
If LastRow >= 3 Then
'Reset results on each pass.
'Assuming -1 is an impossible answer so code knows
'not to put anything on Case Else.
Result = -1
Note = ""
'Cycle through each row and calculate.
For RowCounter = 3 To LastRow
Select Case .Cells(RowCounter, 6) 'Look at column F(column 6) on each row.
Case 10.6 To 200
Result = .Cells(RowCounter, 10)
Note = "No corrections."
Case 3.6 To 10.5
Result = .Cells(RowCounter, 11)
Note = "Correction per ISO16032"
Case 0.1 To 3.5
Result = .Cells(RowCounter, 12)
Note = "Correction limit set to 2,2 dB"
Case Else
Note = "Repeat measurement!"
End Select
'Place results on sheet.
.Cells(RowCounter, 7) = IIf(Result >= 0, Result, "")
.Cells(RowCounter, 8) = Note
Next RowCounter
End If
End With
End Sub
I'm sure this is something obvious but I haven't gotten it yet. Here is my code:
Sub Grossbypercent()
Dim GrossWeight As Double
Dim i As Long
Dim Val As Long
Dim div(2 To 250) As Variant
Dim x As Long
GrossWeight = Application.WorksheetFunction.Sum(Range("H2:H250"))
For x = 2 To 250
Worksheets("Sheet2").Range("B2:B250") = Application.WorksheetFunction.SumIf(Range("Sheet2!A$2:A$250"), Cells(x, 9), Range("H$2:H$250"))
Next x
For i = 2 To 250
Val = Worksheets("Sheet2").Cells(i, 2)
div(i) = Evaluate(Val & "/" & GrossWeight)
Worksheets("Sheet2").Cells(i, 3) = div(i) * Range("O2").Value
On Error Resume Next
Next i
End Sub
I get the titled error on the Evaluate line, before which the code works fine and outputs as requested. I'm going to warrant a guess that I'm declaring something incorrectly, but I'm not sure what, any guidance would be appreciated.
I have a workbook where I want to find the differences of two sheets by looking at the company name and their corporate registration number and then type the differences on the third sheet.
I have tried the code in another workbook with only 143 rows, which works perfectly, but when I try it on the real workbook with 10,000 rows I get a "type mismatch error". Also if I use other columns than the CVR and Firm columns the code also works.
The CVR is numbers and Firms are strings (firm names). I get the
type mismatch error
on the line I marked **. Does somebody know why I get this error?
Sub ComCVR()
Dim CVR1()
Dim CVR2()
Dim Firm1()
Dim Firm2()
Dim n As Long, m As Long
Dim i As Double, j As Double
Dim intCurRow1 As Integer, intCurRow2 As Integer
Dim rng As Range, rng1 As Range
Set rng = ThisWorkbook.Sheets("Last month").Range("A11")
Set rng1 = ThisWorkbook.Sheets("Current month").Range("A11")
n = rng.CurrentRegion.Rows.Count
m = rng1.CurrentRegion.Rows.Count
ReDim CVR1(n)
ReDim Firm1(n)
ReDim CVR2(m)
ReDim Firm2(m)
ThisWorkbook.Sheets("CVR").Range("A1") = "Flyttet CVR"
ThisWorkbook.Sheets("CVR").Range("B1") = "Flyttet Firmanavn"
ThisWorkbook.Sheets("CVR").Range("A1:B1").Interior.ColorIndex = 3
ThisWorkbook.Sheets("CVR").Range("C1") = "Nye CVR"
ThisWorkbook.Sheets("CVR").Range("D1") = "Nye Firmanavn"
ThisWorkbook.Sheets("CVR").Range("C1:D1").Interior.ColorIndex = 4
ThisWorkbook.Sheets("CVR").Range("A1:D1").Font.Bold = True
' Inset data to arrays
For i = 0 To n
CVR1(i) = ThisWorkbook.Sheets("Last month").Cells(12 + i, 5)
Firm1(i) = ThisWorkbook.Sheets("Last month").Cells(12 + i, 4)
Next
For i = 0 To m
CVR2(i) = ThisWorkbook.Sheets("Current month").Cells(12 + i, 5)
Firm2(i) = ThisWorkbook.Sheets("Current month").Cells(12 + i, 4)
Next
intCurRow1 = 2
intCurRow2 = 2
'Old
For i = 0 To n
For j = 0 To m
If Firm1(i) = ThisWorkbook.Sheets("Current month").Cells(12 + j, 4) Then '** Error raised here
Exit For
End If
If j = m Then
ThisWorkbook.Sheets("CVR").Cells(intCurRow1, 1) = CVR1(i)
ThisWorkbook.Sheets("CVR").Cells(intCurRow1, 2) = Firm1(i)
intCurRow1 = intCurRow1 + 1
End If
Next j
Next i
'new
For i = 0 To m
For j = 0 To n
If Firm2(i) = ThisWorkbook.Sheets("Last month").Cells(12 + j, 4) Then
Exit For
End If
If j = n Then
ThisWorkbook.Sheets("CVR").Cells(intCurRow2, 3) = CVR2(i)
ThisWorkbook.Sheets("CVR").Cells(intCurRow2, 4) = Firm2(i)
intCurRow2 = intCurRow2 + 1
End If
Next j
Next i
Columns("A:B").Select
ActiveSheet.Range("$A:$B").RemoveDuplicates Columns:=1, Header:=xlNo
Application.DisplayAlerts = False
Columns("C:D").Select
ActiveSheet.Range("$C:$D").RemoveDuplicates Columns:=1, Header:=xlNo
Application.DisplayAlerts = False
End Sub
Whenever an error happens, the best way is to google it. This is what it says in the documentation of VBA for Type mismatch:
Cause: The variable or property isn't of the correct type. For example, a variable that requires an integer value can't accept a string value unless the whole string can be recognized as an integer.
In the case of the code, it happens, when an array is compared with excel cell. Now the trick - in order to see why it happens, see what is in these:
Debug.Print ThisWorkbook.Sheets("Last month").Cells(12 + i, 4)
Debug.Print Firm1(i)
and the after the error runs, take a look at the immediate window (Ctrl+G). It it quite possible, that there is an error in the excel cell, thus it cannot be compared. This is some easy way to avoid it, if this is the case:
Sub TestMe()
Dim myRange As Range
Set myRange = Worksheets(1).Cells(1, 1)
myRange.Formula = "=0/0"
If Not IsError(myRange) Then
Debug.Print CBool(myRange = 2)
Else
Debug.Print myRange.Address; " is error!"
End If
End Sub
I have the following code that is comparing a combobox on a userform(GUI) to a populated cell on sheet2 of my workbook and I am getting a "type mismatch" error. This was all working until another sub shifted some data into the cells being compared on sheet 2.
My issue lies with if Worksheets(sheet2).cells(1,i).value = LCase(GUI.superCB.Value) then
Worksheets(sheet2).cells(1,i).value Now shows up in the watch as a Variant/Integer which made me think that when the data was shifted it changed the "style" of that cell.
Private Sub NextButton_Click() ''' adds check boxes to frame
Dim i As Integer
'Dim superColm As Integer
For i = 5 To 12
If Worksheets(Sheet2).Cells(1, i).Value = LCase(GUI.superCB.Value) Then 'problem line is right here
superColm = i
Exit For
Else
End If
Next i
NextButton.Visible = False
superCB.Visible = False
Run.Visible = True
Frame1.Visible = True
Dim chk As Control
Dim idx As Integer
Dim lastrow As Integer
lastrow = Worksheets(Sheet2).Cells(Rows.Count, superColm).End(xlUp).Row
For idx = 1 To lastrow - 1
Set chk = GUI.Frame1.Controls.add("Forms.CheckBox.1", idx, True)
'set chk = gui.Frame1.Controls.Add(
chk.Visible = True
chk.Left = 5
chk.Top = (idx - 1) * (chk.Height + 2)
chk.Caption = Cells(idx + 1, superColm) & " " & idx
Next
With Me.Frame1
.ScrollBars = fmScrollBarsVertical
If lastrow <= 10 Then
.ScrollHeight = .InsideHeight * 1.5
ElseIf lastrow <= 15 Then
.ScrollHeight = .InsideHeight * 2.25
ElseIf lastrow <= 20 Then
.ScrollHeight = .InsideHeight * 3
ElseIf lastrow <= 25 Then
.ScrollHeight = .InsideHeight * 3.9
ElseIf lastrow <= 30 Then
.ScrollHeight = .InsideHeight * 4.75
ElseIf lastrow <= 35 Then
.ScrollHeight = .InsideHeight * 5.35
Else
.ScrollHeight = .InsideHeight * 6.25
End If
.ScrollWidth = .InsideWidth * 9
End With
End Sub
If I have sheet 2 as the active sheet Cells(1,i).value will work however, I need to have sheet 2 hidden from the user in the end. With this working it makes me think that the cell style is not the issue.
I have tried going to Excel.Workbooks("Shawn_sch_v1.2.xlsm").worksheets(sheet2).cells(1,i).value and everything down to the base cells() hoping it was missing a sheet reference but nothing has helped.
A String can safely be compared against any other data type in VBA... except Error.
Comparing a Variant/Error against anything will throw a type mismatch error.
This code is implicitly accessing whatever ActiveSheet is:
chk.Caption = Cells(idx + 1, superColm) & " " & idx
Cells should be qualified with the specific Worksheet object you mean to work with. If the active sheet contains a value that can't be coerced into a String (e.g. #VALUE! or #REF!), that will throw a type mismatch error.
Worksheets(Sheet2).Cells(1, i).Value = ...
Here Sheet2 is an identifier. The Worksheets indexer wants either an integer value, or a string. If Sheet2 is the code name of a worksheet in ThisWorkbook, you don't need to dereference it from Worksheets - just use it:
Sheet2.Cells(1, i).Value = ...
The Worksheet class doesn't have a default property, so Debug.Print Worksheets(Sheet2) throws error 438 object doesn't support this property or method - and a subsequent member call like .Cells(1, i), also throws a type mismatch error. If you don't have a Sheet2 string variable holding a worksheet name, I suspect that's the bug you're having right now... which means everything above is just what's waiting to bite you :)
If Sheet2 is a string variable that contains a valid sheet name, you can use the IsError function to verify whether a Variant is a Variant/Error:
If Not IsError(Sheet2.Cells(1, i).Value) Then
' value is safe to compare against a string
Else
' comparing the cell value to anything will throw error 13
End If
Lastly, I would advise against using Rows as a global variable, since it's already a global-scope identifier ([_Global].Rows, implicitly referring to ActiveSheet). Now, renaming that variable with Find/Replace is going to be pretty hard to do without breaking your code: Rubberduck's "rename" refactoring could probably help with doing that safely (disclaimer: I manage that OSS VBIDE add-in project).
This will be fixed with doing a check on the range object data type first.
Worksheets(Sheet2).Cells(1, i).Value is the range object. This can change data types every time the range is modified depending on how it is modified.
LCase(GUI.superCB.Value) This appears to be a form control. If the range is an integer, they cannot compare.
Try something like this:
Dim i As Integer
Dim iRange as String
'Dim superColm As Integer
`This is untested
For i = 5 To 12
iRange = Worksheets(sheet2).Cells(1, i).Text
If iRange = LCase(GUI.superCB.Value) Then 'problem line is right here
superColm = i
Exit For
Else
End If
Next i
The idea is to first be sure that the data types are the same.
You may need to use .Text or .Value2 instead of .Value for the range. If it is possible that the range object will be Empty or Nothing , then you also need to check for those too.
Edit: Changed .Value to .Text
Edit2: This answer is incorrect.
I wrote a function that supposed to get a specific part of a specific Column, and then, by comparing each entry of the column to the value of the cell that is left to it, count the times a specific condition is met.
It all works alright, except one problem.. if I use the function on "Sheet1", get a result and then switch to "Sheet2" and use the function on this sheet it changes the result on "Sheet1" for some reason.
Function countStable(rangeObj As Range) 'rangeObj that being passed is a namedRange(Synamic Range)
Application.Volatile
ActiveSheet.Select
Dim entry, preEntryVal, entryVal As Variant
Dim counters(1 To 5, 1 To 1) As Integer
Dim cStable, cIncreased, cDecreased, cAdded, cLost
cStable = 0
cIncreased = 0
cDecreased = 0
cAdded = 0
cLost = 0
Set rangeObj = Intersect(rangeObj, rangeObj.Parent.UsedRange)
For Each entry In rangeObj
If Not IsEmpty(entry.Value) And Not IsEmpty(ActiveSheet.Range("A" & entry.Row)) Then
entryVal = entry.Value
preEntryVal = ActiveSheet.Cells(entry.Row, entry.Column - 1).Value
If entryVal = preEntryVal Then
cStable = cStable + 1
ElseIf InStr(entryVal, "-") And Not (InStr(preEntryVal, "-")) Then
cLost = cLost + 1
ElseIf Not InStr(entryVal, "-") And InStr(preEntryVal, "-") Then
cAdded = cAdded + 1
ElseIf preEntryVal < entryVal Then
cDecreased = cDecreased + 1
ElseIf preEntryVal > entryVal Then
cIncreased = cIncreased + 1
End If
End If
counters(1, 1) = cStable
counters(2, 1) = cIncreased
counters(3, 1) = cDecreased
counters(4, 1) = cAdded
counters(5, 1) = cLost
Next
countStable = counters
End Function
As commented inside the code, rangeObj that is being passed as parameter was defined in the name manager and it is based on an Offset formula.
I know it changes the values on both sheets because of the dynamic range, but not sure why.. I don't want it to be changed.
Help please?
In several places, the code references the ActiveSheet. Wherever the function appears, it will reflect the value of whatever sheet is active. You'll want to use the parent of the supplied range object instead.
Dim currentSheet as Worksheet
Set currentSheet = rangeObj.Parent
Then, search and replace ActiveSheet with currentSheet in the method.