Is it possible to get a % of (name)/total using VBA? - vba

I was wondering if anyone knows how to get a % of say dctest/In using VBA excel?
dctest for this example is just some tested perimeter while the In can be considered as the total number of devices set to do the dc test.
I have provided an image for better understanding. Both set of results shown are before and after converting the values into %. What i'm required will be finding the % of all tests except for test1-test6. The denominator will be values of In, ie, dctest/In.
I have tried some coding, but am not very sure of how to get the percentage of the values.
Sub macro1()
Dim testrow As Long, testcell As Range, lastrowval As Long
testrow = 1 'initialise
Do
Set testcell = Sheets("Summary").Cells(testrow, 1)
'To look for any test1-test6, if there is do nothing
If testcell = "test1" Or testcell = "test2" Or testcell = "test3" Or testcell = "test4" Or testcell = "test5" Or testcell = "test6" Then
'Do nothing
Exit Do
End If
testrow = testrow + 1
Loop
Do
Set testcell = Sheets("Summary").Cells(testrow, 1)
If testcell = "Resultant" Then
lastrowval = testrow
Exit Do
End If
testrow = testrow + 1
Loop
End Sub
Do take note that, I'm aware of the use of the fixed formula that excel can do, but at times some of the tests such as ACtest will not be there OR there will be an extra test such as morning test, this will affect the fixed formula.
Do take note also that test1 - test6, sometimes all 6 tests will be present, at times only 1 test will be present.
What my concern is if there is an extra test say test1 added in, the row from dctest..... Resultant will be shifted down by 1 row.
And if there is a test say test6 got removed, the row from dctest.... resultant will be shifted up by 1 row. I'm not very sure what can be done to make sure this issues can be solved.
Follow up

I have taken the same example as shown in the picture. Like your previous question, you can either use a formula or a code.
Formula
In Cell J7, enter this formula and then right click on the cell to format as %age.
=OFFSET(INDIRECT(ADDRESS(INDEX(MATCH(I7,$A$1:$A$11,0),1,0),1,1,1)),0,1)/$B$1
If you need then simply copy the formula down
Code
Sub Sample()
With Range("J7")
.FormulaR1C1 = _
"=OFFSET(INDIRECT(ADDRESS(INDEX(MATCH(RC[-1],R1C1:R11C1,0),1,0),1,1,1)),0,1)/R1C2"
.NumberFormat = "0.00%"
End With
End Sub
OR
Sub Sample()
With Range("J7")
.Formula = _
"=OFFSET(INDIRECT(ADDRESS(INDEX(MATCH(I7,$A$1:$A$11,0),1,0),1,1,1)),0,1)/$B$1"
.NumberFormat = "0.00%"
End With
End Sub
FOLLOWUP
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim SearchText As String, Excludetext As String
Dim LastRow As Long, i As Long, j As Long
Dim MyArray() As String
Dim boolContinue As Boolean
'~~> Add/Remove the text here which you want to ignore
Excludetext = "In,Test1,Test2,Test3,Test4,Test5,Test6"
MyArray = Split(Excludetext, ",")
Set ws = Sheets("Sheet1")
LastRow = ws.Range("I" & Rows.Count).End(xlUp).Row
For i = 1 To LastRow
boolContinue = True
For j = 0 To UBound(MyArray)
SearchText = Ucase(Trim(MyArray(j)))
If Ucase(Trim(ws.Range("I" & i).Value)) = SearchText Then
boolContinue = False
Exit For
End If
Next j
If boolContinue = True Then
With Range("J" & i)
.Formula = _
"=OFFSET(INDIRECT(ADDRESS(INDEX(MATCH(I" & i & _
",$A$1:$A$11,0),1,0),1,1,1)),0,1)/$B$1"
.NumberFormat = "0.00%"
End With
End If
Next i
End Sub
HTH
Sid

Why don't you just turn it into a percent part/whole=x/100

Related

Excel VBA Large Table, Add Comments Vlookup, After Hitting Command Button

I have a large table and the information I'm wanting to add comments to falls within Range(D11:CY148). I have two tabs - "Finish Matrix" (main) and "list" (hidden - has 2 columns).
I have two issues.
First issue - Code works to a degree, after I type my values within a cell it automatically adds comments based off info in another sheet. The problem is there is too many cells to be manually typing into and if I copy and paste the code doesn't run. I created a CommandButton and wanted it to refresh the entire table with comments depending if the cells had the values that fall within "list". I tried to create a call out to Worksheet_Change but to no avail. (I'm a beginner so it'll help if you explain)
Second issue - I'm assuming it'll get fixed with whatever suggestion that works. Occasionally after typing into a cell I would get an error. Can't remember the error name but it is one of the common ones, atm the error isn't popping up but surely it'll come back since I didn't do anything different to the code.
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Columns("A:CX")) Is Nothing Then _
If Intersect(Target, Columns("CY")) Is Nothing Then Exit Sub
Dim lRow As Integer
lRow = Sheets("list").Range("A1").End(xlDown).Row
If Target.Value = vbNullString Then Target.ClearComments
For Each cell In Sheets("list").Range("A1:A" & lRow)
If cell.Value = Target.Value Then
Target.AddComment
Target.Comment.Text Text:=cell.Offset(0, 1).Value
End If
Next cell
End Sub
Thanks for any and all help!
You are basically missing the For Each Cell in Target part...
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim wsMain As Worksheet, wsList As Worksheet
Dim cell As Range
Dim vCommentList As Variant
Dim i As Long, lLastRow As Long
Dim sValue As String
On Error GoTo ErrHandler
Application.ScreenUpdating = False
Application.EnableEvents = False
Set wsMain = Target.Parent
Set Target = Intersect(Target, wsMain.Range("D11:CY148"))
If Target Is Nothing Then Exit Sub
Set wsList = wsMain.Parent.Sheets("list")
lLastRow = LastRow(1, wsList)
' Read Comment List into Variant (for speed)
vCommentList = wsList.Range("A1:B" & lLastRow)
Target.ClearComments
' This...For each Cell in Target...is what you were missing.
For Each cell In Target
sValue = cell
For i = 1 To UBound(vCommentList)
If sValue = vCommentList(i, 1) Then
AddComment cell, CStr(vCommentList(i, 2))
Exit For
End If
Next
Next
ErrHandler:
If Err.Number <> 0 Then Debug.Print Err.Description
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Proper way to find last row ...
Public Function LastRow(Optional Col As Integer = 1, Optional Sheet As Excel.Worksheet) As Long
If Sheet Is Nothing Then Set Sheet = Application.ActiveSheet
LastRow = Sheet.Cells(Sheet.Rows.Count, Col).End(xlUp).Row
End Function
Add Comment Sub the allows appending is needed...
Public Sub AddComment(Target As Range, Text As String)
If Target.Count = 1 Then
If Target.Comment Is Nothing Then
Target.AddComment Text
Else
Target.Comment.Text Target.Comment.Text & vbLf & Text
End If
End If
End Sub
Untested, but this will take all the values in Range(D11:CY148) and add a comment based on a lookup from Sheet "list".
Sub testy()
Dim arr As Variant, element As Variant
Dim i As Long, j As Long, listItems As Long, rwLast As Long, clLast As Long
Dim comm As String
Dim rng As Range, cell As Range
listItems = Sheets("list").Range("A1").End(xlDown).Row
rwLast = Cells.SpecialCells(xlCellTypeLastCell).Row ' Adjust to fit your needs
clLast = Cells.SpecialCells(xlCellTypeLastCell).Column 'Idem
Set rng = Sheets("list").Range("A1:A" & listItems)
arr = Range("D11:CY148").Value
With Worksheets("Finish Matrix")
For i = 1 To rwLast - 10 'Adjust to make it more general, this is pretty rough
For j = 1 To clLast - 3 'Idem
If i = 3 Then
End If
comm = ""
For Each cell In rng
If arr(i, j) = cell.Value Then
comm = comm & Chr(13) & cell.Offset(0, 1).Value
End If
Next cell
If Not (comm = "") Then
.Cells(10, 3).Offset(i, j).ClearComments
.Cells(10, 3).Offset(i, j).AddComment
.Cells(10, 3).Offset(i, j).Comment.Text Text:=comm
End If
Next j
Next i
End With
End Sub

Excel vba to select next option in autofilter drop down menu

I have several column with a few hundred rows of data. One of my roles is to look through the data (most commonly in column 2), So what I do is click the little drop down arrow on the column header to open the auto filter list, deselects the first value, then select the next value. Then, likewise, open menu, deselect second value and select third.
There's no fixed number of values either. Different data sheets have varying amounts of data. The data usually goes like 0,10,40,50,60,.... Again it isn't fixed. It is an array however. All the data is in increasing order already.
What I need:
Preferably a button to click (for column 2) that deselects the currently selected value, selects the next value and filters that out
The converse. I.e. Deselects the current value, selects the previous value
Essentially I need a Forward and Back button for my data.
This is what I get when I tried to record my actions.
Sub a()
ActiveSheet.Range("$A$2:$V$609").AutoFilter Field:=2, Criteria1:
="750385/000"
ActiveSheet.Range("$A$2:$V$609").AutoFilter Field:=2, Criteria1:
="750385/010"
ActiveSheet.Range("$A$2:$V$609").AutoFilter Field:=2, Criteria1:
="750385/017"
End Sub
Appreciate any help!!
There is a method to read out the curent filter, from which on you can loop through the column untill you find that value. here you just need to jump to the value in the next row, which now you can put into the filter.
So in conclusion this method would be your "forward"-button
Sub test()
Dim startRow As Integer
startRow = 2
Dim rangeString As String
rangeString = "$A$2:$V$609"
Dim rng As Range
Set rng = Range(rangeString)
Dim currentCrit As String
currentCrit = rng.Parent.AutoFilter.Filters(2).Criteria1
currentCrit = Right(currentCrit, Len(currentCrit) - 1)
Dim i As Integer
For i = startRow To startRow + rng.Rows.Count
If Cells(i, 2).Value = currentCrit Then
i = i + 1
Exit For
End If
Next
If i > rng.Rows.Count + startRow Then
Exit Sub
End If
ActiveSheet.Range(rangeString).AutoFilter Field:=2, Criteria1:=Cells(i, 2).Value
End Sub
Note: This won´t work if there are duplicates in you column B, if this is so replace the part with the For-Loop with the following:
Dim i As Integer
Dim bool As Boolean
bool = False
For i = startRow To startRow + rng.Rows.Count
If Cells(i, 2).Value = currentCrit Then
bool = True
End If
If bool And Cells(i, 2).Value <> currentCrit Then
Exit For
End If
Next
Hope I could help.
I would use Spinbuttons on the sheet and link them to the first cell of the column, it want to filter.
(I called it spbFilterChange and linked it to $B$1)
(picture upload doesnt work here, sorry)
Then you can put the following code in the module of your worksheet:
Private Sub spbFilterChange_SpinDown()
Change_Filter Me.Range(Me.spbFilterChange.LinkedCell), False
End Sub
Private Sub spbFilterChange_SpinUp()
Change_Filter Me.Range(Me.spbFilterChange.LinkedCell), True
End Sub
And the following sub in a standard module:
Option Explicit
Sub Change_Filter(SortField As Range, Up As Boolean)
Dim Filter_Values As Collection
Dim Value_Arr, Val, Sort_Value As String
Application.ScreenUpdating = False
' Find Unique Values in relevant Column -> Collection
Set Filter_Values = New Collection
SortField.Offset(2, 0).Areas(1).AutoFilter SortField.Column
Value_Arr = SortField.Parent.Range(SortField.Offset(3, 0), SortField.Parent.Cells(SortField.Parent.Rows.Count, SortField.Column).End(xlUp)).Value2
On Error Resume Next
For Each Val In Value_Arr
Filter_Values.Add Val, CStr(Val)
Next Val
' Check if Value of LinkedCell is in range
If SortField.Value < 1 Or SortField.Value > Filter_Values.Count Then SortField.Value = 1
' set autofilter
Sort_Value = Filter_Values(SortField.Value)
SortField.Offset(2, 0).AutoFilter SortField.Column, Sort_Value
Application.ScreenUpdating = True
End Sub
This should solve your problem and could be used on different columns and sheets (you have to add another copy of the event-procedures in the worksheet-module).
I would do something like this.
First: Get Help column X where you copy all the Unique data from column B for example.
Option Explicit
Sub CreateUniqueList()
Dim lastrow As Long
lastrow = Cells(Rows.Count, "B").End(xlUp).Row
ActiveSheet.Range("B1:B" & lastrow).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=ActiveSheet.Range("X1"), _
Unique:=True
ActiveSheet.Range("Y1").Value = "x"
End Sub
Your list could lokk after that like this:
After that, you would need a loop for the buttons:
Something like this.
//The Code is not Testet//
Sub butNextValue()
Dim lastrow As Long
lastrow = Cells(Rows.Count, "B").End(xlUp).Row
For i = 2 To lastrow
If ActiveSheet.Cells(i, 25).Value = "x" Then
If Not ActiveSheet.Cells(i+1, 24)-value = "" Then 'check if next value is there
ActiveSheet.Range("$A$2:$V$609").AutoFilter Field:=2, Criteria1:=ActiveSheet.Cells(i+1, 24)-value
Else
MsgBox "No more Next Values"
End If
Exit For
End If
Next i
End Sub
Sub butPriValue()
Dim lastrow As Long
lastrow = Cells(Rows.Count, "B").End(xlUp).Row
For i = 2 To lastrow
If ActiveSheet.Cells(i, 25).Value = "x" Then
If Not ActiveSheet.Cells(i-1, 24)-value = "Set" OR Not ActiveSheet.Cells(i-1, 24)-value = "" Then 'check if next value is there
ActiveSheet.Range("$A$2:$V$609").AutoFilter Field:=2, Criteria1:=ActiveSheet.Cells(i-1, 24)
Else
MsgBox "No more Pri Values"
End If
Exit For
End If
Next i
End Sub

Excel vba excessive run time, but no issues using step into

I have some code to delete duplicate rows while keeping the first instance the particular string appears.
When I step into the code to delete duplicates, the macro runs without a hitch. However, once I hit run macro, my excel freezes and stops responding. I'm not quite sure why...
If anyone could shed some light. That would be greatly appreciated. (Also I added a breakline to show where I attempt to run to).
Sub CleanUp()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim lastRow As Integer
Dim i As Integer, j As Integer, k As Integer
Dim stakedItem As String
Dim sortCell As Range, allCell As Range, sortcell2 As Range
Dim currentItem As String, baseItem As String
lastRow = Sheet2.Range("A" & Sheet2.Rows.Count).End(xlUp).Row
Set sortCell = Sheet2.Range("A1")
Set sortcell2 = Sheet2.Range("B1")
Set allCell = Sheet2.Range("A1:Z" & lastRow + 1)
baseItem = Sheet2.Range("B2")
allCell.Sort key1:=sortcell2, order1:=xlAscending, Header:=xlYes
For i = 3 To lastRow
currentItem = Sheet2.Range("B" & i)
If currentItem = baseItem Then
Sheet2.Rows(i).Delete
i = i - 1
lastRow = lastRow - 1
Else
baseItem = Sheet2.Range("B" & i)
End If
Next i
Breakline here...
allCell.AutoFilter field:=2, Criteria1:=Array("*G*", "*HUB*"), Operator:=xlFilterValues
allCell.Sort key1:=sortCell, order1:=xlAscending, Header:=xlYes
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Thank you!
Francis
This will never exit if it finds a row to delete. The reason is that in a For loop, the exit condition is fixed when that line of code evaluates. That means lastRow will always be whatever it was when you entered the loop.
This code illustrates:
Sub example()
Dim x As Long, i As Long
x = 5
For i = 1 To x
Debug.Print i 'This runs 5 times...
x = 1 '...even though you change x here.
Next
End Sub
So, the only thing lastRow = lastRow - 1 does is decrement the variable. After you've deleted the first row, you're guaranteed that at the end of the sheet that currentItem = BaseItem. And inside that condition, you decrement the loop counter, which gives you an infinite loop.
As #TimWilliams mentions in the comments, you should loop backward if you are deleting rows. Something like this:
For i = lastRow To 3 Step -1
currentItem = Sheet2.Range("B" & i)
If currentItem = BaseItem Then
Sheet2.Rows(i).Delete
Else
BaseItem = Sheet2.Range("B" & i)
End If
Next i

Need help trimming spaces out of column

I am trying to figure out how to loop through the first column of my worksheet and take out the spaces so I can use VLOOKUP. Not sure how to do it in VBA. Here is what I have:
I can't figure out why it does not go onto the next sheet now? I can't just cycle through all of the sheets since they are different.
Sub trima()
Dim x As Integer
Dim numrows As Long
numrows = Range("A1",Range("A1").End(xlDown)).Rows.Count
Range("A1").Select
For x = 1 To numrows
Application.WorksheetFunction.trim (ActiveCell)
ActiveCell.Offset(1, 0).Select
Next
End Sub
Here you go:
Sub TrimA()
Dim v
v = [transpose(transpose(trim(a1:index(a:a,match("",a:a,-1)))))]
[a1].Resize(UBound(v)) = v
End Sub
UPDATE
If you want to update multiple sheets, you can utilize the above like so:
Sub DoTrims()
Sheet1.Activate: TrimA
Sheet2.Activate: TrimA
'etc.
End If
The Trim function does not work like that.
Instead, try something like:
Sub trima()
Dim numrows As Long
Dim vItem as Variant
Dim i As Long
numrows = Range("A1",Range("A1").End(xlDown)).Rows.Count
Application.ScreenUpdating = False
With ActiveSheet
For i = 1 To numrows
vItem = .Range("A" & i)
If vItem <> vbNullString then .Range("A" & i) = Application.WorksheetFunction.Trim(vItem)
Next
End With
Application.ScreenUpdating = True
End Sub
The following code will loop through ALL worksheets in the Workbook and perform the same trim on values in Column A:
Sub trimA()
Dim ws As Excel.Worksheet
Dim i As Long, numrows As Long
Dim vItem As Variant
Application.ScreenUpdating = False
For Each ws In Worksheets
With ws
numrows = .Range("A1", .Range("A1").End(xlDown)).Rows.Count
For i = 1 To numrows
vItem = .Range("A" & i)
If vItem <> vbNullString Then .Range("A" & i) = Application.WorksheetFunction.Trim(vItem)
Next i
End With
Next
Application.ScreenUpdating = True
End Sub
Using the Range.TextToColumns method should quickly clear all cells containing leading/trailing spaces.
This procedure can quickly convert text-that-look-like-numbers to true numbers as well.
Dim c As Long
With Range("A1").CurrentRegion `<~~ set to the desired range of one or more columns
For c = 1 To .Columns.Count
.Columns(c).TextToColumns Destination:=.Columns(c), _
DataType:=xlFixedWidth, FieldInfo:=Array(0, 1)
Next c
End With
If the cells actually contain non-standard spacing like the non-breaking space (common on data copied from a web page) then other Range.Replace method should be added.

Finding the LastRow in multiple column ranges?

I'm trying to find the LastRow in multiple column ranges ignoring certain columns... I have two attempts but can't get either working correctly:
BOTH Examples are ignoring columns N and O
My first attempt is the following, however it doesn't get the correct last range, if I have something in A15 for example and T10, it thinks the last row is 10 when it should be 15.
Sub LastRowMacro()
LastRowString = "A1:M" & Rows.Count & ", P1:Z" & Rows.Count
LastRowTest = Range(LastRowString).Find(What:="*", After:=[A1], SearchOrder:=xlByRows, searchDirection:=xlPrevious).Row
End Sub
My second attempt is as follows, but it seems rather long winded.
Sub LastRowMacro()
Dim i As Long
LastRow = 1
IgnoreColumnList = "N;O"
For i = 1 To Cells(1, Columns.Count).End(xlToLeft).Column
ColumnLetter = Split(Cells(1, i).Address(True, False), "$")(0)
For Each varFind In Split(IgnoreColumnList, ";")
If varFind = ColumnLetter Then
varNotFound = False
Exit For
End If
varNotFound = True
Next
If varNotFound Then
CurrentLastRow = Cells(Rows.Count, i).End(xlUp).Row
If CurrentLastRow >= LastRow Then
LastRow = CurrentLastRow
End If
varNotFound = False
End If
Next
End Sub
Ideally I'd like my first attempt to work however if it just doesn't work then someone surely can improve my second version...
Try this
*There is an ignoreList variable with all the columns that you want to ignore. Make sure you populate this correctly - currently ignoring N, O, P
*You may need to set the sh variable to the correct sheet - currently it's Sheet1
*btw. this snippet will always find the last last row on the spreadsheet. you can add another elseif to check whether there are 2 columns with the same high last row in case there was 2 columns with the highest lastRows.
Sub FindingLastRow() ' ignoring some columns
Dim ignoreList
ignoreList = Array("N", "O", "P") ' MODIFY IGNORE LIST
Dim sh As Worksheet
Set sh = Sheet1 ' SET CORRECT SHEET
Dim currentlast As Range
Set currentlast = sh.Cells(1, 1)
Dim iteratingCell As Range
With sh
For j = 1 To .UsedRange.Columns.Count
Set iteratingCell = .Cells(1, j)
If Not isIgnored(iteratingCell, ignoreList) Then
If iteratingCell.Cells(Rows.Count).End(xlUp).Row >= currentlast.Cells(Rows.Count).End(xlUp).Row Then
Set currentlast = iteratingCell
End If
End If
Next
Set currentlast = .Range("$" & Split(currentlast.Address, "$")(1) & "$" & currentlast.Cells(Rows.Count).End(xlUp).Row)
End With
MsgBox currentlast.Address
End Sub
Function isIgnored(currentlast As Range, ignoreList As Variant) As Boolean
Dim ignore As Boolean
Dim letter As Variant
For Each letter In ignoreList
If StrComp(Split(currentlast.Address, "$")(1), letter, vbTextCompare) = 0 Then
ignore = True
Exit For
End If
Next
isIgnored = ignore
End Function