Including a counter within an advanced filter - excel

I have an advanced filter set up and separate to that subroutine I have another subroutine that loops through the filter results and places the value into a text box. That all works fine but I'm trying to include a counter so for every result that's being displayed the counter goes up, and then when the criteria changes and new results are found it goes back to 1 and counts again from there.
Public Sub GetNextResult()
FilterData
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Database")
Dim header As String
header = "txtbox1"
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim DataRange As Range
Set DataRange = ws.Range("A5", "H" & LastRow)
Dim FilteredData As Range
Set FilteredData = DataRange.Resize(ColumnSize:=1).SpecialCells(xlCellTypeVisible)
If CurrentRow + 1 > FilteredData.Cells.Count Then
CurrentRow = 1
End If
CurrentRow = CurrentRow + 1
Dim i As Long
Dim cell As Variant
Dim counter As Integer
counter = 1
For Each cell In FilteredData
i = i + 1
If i = CurrentRow Then
Call ShowAll
TextboxName = "txtbox1"
ActiveSheet.Shapes(TextboxName).DrawingObject.Text = cell.Offset(0, 2)
TextboxName2 = "txtbox2"
ActiveSheet.Shapes(TextboxName2).DrawingObject.Text = cell.Offset(0, 3)
TextboxName3 = "Cardcounter"
ActiveSheet.Shapes(TextboxName3).DrawingObject.Text = counter
If ActiveSheet.Shapes(TextboxName).DrawingObject.Text = header Then
'MsgBox "header detected"
'Exit Sub
Call GetNextResult
End If
Call quick_artwork
counter = counter + 1
Else
Call ShowAll
'Exit Sub
'MsgBox "No data found matching this criteria"
'TextboxName = "txtbox1"
'ActiveSheet.Shapes(TextboxName).DrawingObject.Text = "No data found matching this criteria"
'TextboxName2 = "txtbox2"
'ActiveSheet.Shapes(TextboxName2).DrawingObject.Text = ""
End If
Next cell
End Sub
So I've put counter = 1 before the for loop and then within the for loop I've put counter = counter + 1 and I thought that would work but it doesn't. It doesn't show an error, but the number doesn't change.
Any help would be appreciated!

You could declare counter outside the GetNextResult() routine, and set it to 1 before calling the GetNextResult() routine.
Dim counter As Integer
Sub SomeOtherSubCallingGetNextResult()
...
counter = 1
GetNextResult
...
End sub

Related

Replacing pos,neg values to another sheet

Screenshot#1
So i have to replace positive & negative numbers in column "A", from sheet "1" to sheet second[positive] and third sheet[negative].
Here is what i tried:
Sub Verify()
Dim row As Long
For row = 1 To 20
If ActiveSheet.Cells(row,1) <> "" Then
If ActiveSheet.Cells(row,1) > 0 Then
ActiveSheet.Cells(row,2) = ActiveSheet.Cells(row,1)
End If
End If
Next
End Sub
Here is what that program do:
Screenshot#2
So as we see i am getting positive values in column "B" sheet 1.
Your code is not currently working because you are only using ActiveSheet, rather than placing data on other worksheets as required. Below is some VBA code that loops column A in your original sheet, and outputs the data to column A in two different sheets as required:
Sub sSplitPositiveNegative()
Dim wsOriginal As Worksheet
Dim wsPositive As Worksheet
Dim wsNegative As Worksheet
Dim lngLastRow As Long
Dim lngPositiveRow As Long
Dim lngNegativeRow As Long
Dim lngLoop1 As Long
Set wsOriginal = ThisWorkbook.Worksheets("Original")
Set wsPositive = ThisWorkbook.Worksheets("Positive")
Set wsNegative = ThisWorkbook.Worksheets("Negative")
lngLastRow = wsOriginal.Cells(wsOriginal.Rows.Count, "A").End(xlUp).Row
lngNegativeRow = 2
lngPositiveRow = 2
For lngLoop1 = 1 To lngLastRow
If wsOriginal.Cells(lngLoop1, 1).Value > 0 Then
wsPositive.Cells(lngPositiveRow, 1) = wsOriginal.Cells(lngLoop1, 1)
lngPositiveRow = lngPositiveRow + 1
Else
wsNegative.Cells(lngNegativeRow, 1) = wsOriginal.Cells(lngLoop1, 1)
lngNegativeRow = lngNegativeRow + 1
End If
Next lngLoop1
Set wsPositive = Nothing
Set wsNegative = Nothing
Set wsOriginal = Nothing
End Sub
You will need to change the names of the worksheets referenced in the code to match those in your workbook.
Regards
Made the code a little reusable for you. Feel free to change sheet names or the last_row variable. The last_pos_val and last_neg_val are used so you won't have empty rows on the second and third sheet. You didn't specify what to do with zero, so it's currently added to the negative sheet.
Sub Verify()
Dim row As Long, last_row As Long, last_pos_val As Long, last_neg_val As Long
Dim ws_source As Worksheet, ws_pos As Worksheet, ws_neg As Worksheet
Dim wb As Workbook
Set wb = ThisWorkbook
Set ws_source = wb.Sheets("Sheet1")
Set ws_pos = wb.Sheets("Sheet2")
Set ws_neg = wb.Sheets("Sheet3")
last_pos_val = 1
last_neg_val = 1
last_row = 20
For row = 1 To last_row
If ws_source.Cells(row,1) <> "" Then
If ws_source.Cells(row,1) > 0 Then
ws_pos.Cells(last_pos_val,1) = ws_source.Cells(row,1)
last_pos_val = last_pos_val + 1
Else
ws_neg.Cells(last_neg_val,1) = ws_source.Cells(row,1)
last_neg_val = last_neg_val + 1
End If
End If
Next
End Sub
Split Positive & Negative
Adjust the values in the constants section.
Both subs are needed. The first sub calls the second one.
The Code
Option Explicit
Sub SplitPN()
Const Source As String = "Sheet1"
Const Positive As String = "Sheet2"
Const Negative As String = "Sheet3"
Const FirstRow As Long = 1
Const SourceColumn As Long = 1
Const PositiveFirstCell As String = "A1"
Const NegativeFirstCell As String = "A1"
Dim rngSource As Range
Dim rngPositive As Range
Dim rngNegative As Range
With ThisWorkbook
With .Worksheets(Source)
Set rngSource = .Columns(SourceColumn).Find(What:="*", _
LookIn:=xlFormulas, SearchDirection:=xlPrevious)
If rngSource Is Nothing Then Exit Sub
If rngSource.Row < FirstRow Then Exit Sub
Set rngSource = .Range(.Cells(FirstRow, SourceColumn), rngSource)
End With
Set rngPositive = .Worksheets(Positive).Range(PositiveFirstCell)
Set rngNegative = .Worksheets(Negative).Range(NegativeFirstCell)
End With
SplitPosNeg rngSource, rngPositive, rngNegative
End Sub
Sub SplitPosNeg(SourceRange As Range, PositiveFirstCell As Range, _
NegativeFirstCell As Range)
Dim Source, Positive, Negative
Dim UB As Long, i As Long
Source = SourceRange
UB = UBound(Source)
ReDim Positive(1 To UB, 1 To 1)
ReDim Negative(1 To UB, 1 To 1)
For i = 1 To UBound(Source)
Select Case Source(i, 1)
Case Is > 0: Positive(i, 1) = Source(i, 1)
Case Is < 0: Negative(i, 1) = Source(i, 1)
End Select
Next
PositiveFirstCell.Resize(UB) = Positive
NegativeFirstCell.Resize(UB) = Negative
End Sub

Excel VBA Code pastes result into wrong range

A script that copies a range into another range. However, when I try to copy the range from Sheet1 to Sheet2 the result won't be pasted into column J, it get pasted with an offset of 8 columns (column R). I cant understand why? Both RowCountSummary and ColumnCountSummary are set to 0, i.e. first index of the range?
Sub InsertForecastData()
Dim ColumnsCount As Integer
Dim ColCounter As Integer
Dim RowsCount As Integer
Dim ForeCastRange As Range
Dim ForecastWS As Worksheet
Dim SummaryWs As Worksheet
Dim PasteRange As Range
Dim ColumnCountSummary As Integer
Dim RowCountSummary As Integer
ColumnsCount = 300
ColCounter = 0
RowsCount1 = 0
RowsCount2 = 47
ColumnCountSummary = 0
RowCountSummary = 0
Do While ColCounter <= ColumnsCount
Worksheets("Sheet1").Select
Set ForeCastRange = Worksheets("Sheet1").Range("B2:KN49")
With ForeCastRange
.Range(.Cells(RowsCount1, ColCounter), .Cells(RowsCount2, ColCounter)).Copy
End With
Worksheets("Sheet2").Select
Set PasteRange = Worksheets("Sheet2").Range("J2:J13915")
With PasteRange
.Range(.Cells(RowCountSummary, ColumnCountSummary), .Cells(RowCountSummary + RowsCount2, ColumnCountSummary)).PasteSpecial
End With
RowCountSummary = RowCountSummary + 48
ColCounter = ColCounter + 1
Loop
End Sub
This behaviour has been encountered before and can seen with this simple demo
Sub test()
With Sheet1.Range("J3:J100")
Debug.Print .Range(.Cells(0, 0), .Cells(47, 0)).Address
End With
End Sub
which results in $R$4:$R$51. If you repeat run for the columns B to J the results are B,D,F,H,J,L,N,P showing the doubling effect. B is OK I think because of the zero column number.
You can probably fix your code by setting RowCountSummary = 1 and ColumnCountSummary = 1 and adding .parent
With PasteRange
.Parent.Range(.Cells(RowCountSummary, ColumnCountSummary), _
.Cells(RowCountSummary + RowsCount2, ColumnCountSummary)).PasteSpecial
End With
or you could try this
Sub InsertForecastData1()
Const columnCount As Integer = 3
Const rowCount As Integer = 48
Const sourceCol As String = "B"
Const targetCol As String = "J"
Const startRow As Integer = 2
Const records As Integer = 300
Dim rngSource as Range, rngTarget As Range
Dim start as Single, finish as Single
Set rngSource = Worksheets("Sheet1").Range(sourceCol & startRow)
Set rngSource = rngSource.Resize(rowCount, columnCount)
Set rngTarget = Worksheets("Sheet2").Range(targetCol & startRow)
start = Timer
Application.ScreenUpdating = False
Dim i As Integer
For i = 1 To records
'Debug.Print rngSource.Address, rngTarget.Address
rngSource.Copy rngTarget
Set rngSource = rngSource.Offset(rowCount, 0)
Set rngTarget = rngTarget.Offset(rowCount, 0)
Next i
Application.ScreenUpdating = True
finish = Timer
MsgBox "Completed " & records & " records in " & finish - start & " secs"
End Sub
See Remarks section the docs

Excel VBA: Assigning a variable to the value of the first cell from results of an AdvancedFilter

I'm trying to get the value of the first filter result that is in column C. The headers are on A5 to J5 and then the results are below. Pointing to C6 gives me the value of the first row in the database when it isn't filtered. I've read online that using
Range("C" & .Offset(1, 0).SpecialCells(xlCellTypeVisible)(1).Row).Value
Would return the value as this takes into account that some of the rows are filtered out, but this doesn't work for me. I tried putting it in my GetNextResult subroutine below where I have Set the FilteredData Range variable, and it prompts Compile Error, Invalid or Unqualified Reference.
Just to make it clear, the first subroutine, FilterData, is actually the one that filters the data. The second subroutine, GetNextResult, calls on FilterData() but the purpose of this subroutine is to insert the value of each result into a textbox, one by one upon each execution of this macro.
Not that it's particularly relevant to the issue but in case it's important, the reason I want the value of the first result when filtered is for the purposes of a counter. I'm looking to make it so every time the GetNextResult macro sends the next result to the text boxes the counter goes up by one, so that the user can keep track of how many they've cycled through and not get to the point where they don't realise they're seeing the same results over and over. I figured, if I can get the value of the first filtered row then I can make an if statement say that if the textbox contains this value then make sure counter is 1, and that is the reset point.
Public Sub FilterData()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Database")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim CriteriaRange As Range
Set CriteriaRange = ws.Range("A2", "C3")
If Range("C3").Value = "Any" Then
Set CriteriaRange = ws.Range("A2", "B3")
End If
Dim DataRange As Range
Set DataRange = ws.Range("A5", "J" & LastRow)
DataRange.AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=CriteriaRange, Unique:=False
Call last_used_sort
If Not DataRange.Columns(1).Rows.SpecialCells(xlCellTypeVisible).Count > 1 Then
Call ShowAll
MsgBox "No Results"
Exit Sub
End If
End Sub
Public Sub GetNextResult()
FilterData
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Database")
Dim header As String
header = "Cards"
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim DataRange As Range
Set DataRange = ws.Range("A5", "J" & LastRow)
Dim FilteredData As Range
Set FilteredData = DataRange.Resize(ColumnSize:=1).SpecialCells(xlCellTypeVisible)
If CurrentRow + 1 > FilteredData.Cells.Count Then
CurrentRow = 1
End If
CurrentRow = CurrentRow + 1
With FilteredData
first_row = Range("C" & .Offset(1, 0).SpecialCells(xlCellTypeVisible)(1).Row).Value
End With
Debug.Print first_row
Dim i As Long
Dim cell As Variant
Static counter As Long
counter = counter + 1
If counter = Quick_Insert_Range Then
counter = 1
End If
ActiveSheet.Shapes("Cardcounter").TextFrame.Characters.Text = counter
For Each cell In FilteredData
i = i + 1
If i = CurrentRow Then
Call ShowAll
TextboxName = "txt1"
ActiveSheet.Shapes(TextboxName).DrawingObject.Text = cell.Offset(0, 2)
TextboxName2 = "txt2"
ActiveSheet.Shapes(TextboxName2).DrawingObject.Text = cell.Offset(0, 3)
TextboxName3 = "txt3"
ActiveSheet.Shapes(TextboxName3).DrawingObject.Text = cell.Offset(0, 4)
If ActiveSheet.Shapes(TextboxName).DrawingObject.Text = header Then
Call GetNextResult
End If
Call quick_artwork
Else
Call ShowAll
End If
Next cell
End Sub
In your second code, you did not CALL Filter Data. Your first 2 lines of code should be
Public Sub GetNextResult()
Call FilterData

Moving through sequential array items

The code below creates an array of unique values from values in Column A. Each selected array element is used to select a range on the sheet. The range is displayed in a userform Listbox.
I would like help with code that would allow the user to scroll through each array ‘MyarUniqVal’ element via two form buttons Right ‘>>’ and Left ‘<<’. Each time a button is pressed a sequential array item will be selected and a new range will populate the Listbox.
Any help would be greatly appreciated.
Thanks,
Please see the code below:
Sub testRange3()
Dim lastrow, i, j As Long
Dim c As Range, rng As Range
Dim MyArUniqVal() As Variant
ReDim MyArUniqVal(0)
'With ActiveSheet
With ThisWorkbook.Worksheets("Temp")
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 1 To lastrow
If .Cells(i, 1).Value <> .Cells(i + 1, 1).Value Then
MyArUniqVal(UBound(MyArUniqVal)) = .Cells(i, 1).Value
ReDim Preserve MyArUniqVal(UBound(MyArUniqVal) + 1)
End If
Next
ReDim Preserve MyArUniqVal(UBound(MyArUniqVal) - 1)
End With
For j = LBound(MyArUniqVal) To UBound(MyArUniqVal)
'Prints out each array to Immediate Window
Debug.Print j
'Prints out unique values from Column A stored in array to Immediate Window
Debug.Print MyArUniqVal(j)
Next
With ThisWorkbook.Worksheets("Temp")
'changed to ActiveSheet
'With ActiveSheet
For Each c In .Range("A1:A" & lastrow)
For j = LBound(MyArUniqVal) To UBound(MyArUniqVal)
If UCase(c.Text) = j Then
'If UCase(c.Text) = "B" Then
If rng Is Nothing Then
Set rng = .Range("B" & c.Row).Resize(, 2)
Debug.Print rng
Else
Set rng = Union(rng, .Range("B" & c.Row).Resize(, 2))
Exit For
Debug.Print rng
End If
End If
Next
Next c
End With
If Not rng Is Nothing Then rng.Select
End Sub
See the following code to get you heading the the right direction. I took the approach of adding another listbox that displayed the available prefixes to help the user see what was available and then searching the data column for entries containing the selected prefix.
Hopefully you will be able to adapt the name of the variables and objects to whatever you are currently using. Let me know if anything needs clarification. Best of luck with your project.
My sample form code:
Private Sub cmdBack_Click()
code_frmMain.IncrementValue (0)
End Sub
Private Sub cmdNext_Click()
code_frmMain.IncrementValue (1)
End Sub
Private Sub lstPrefixes_Change()
code_frmMain.DisplayNext
End Sub
Private Sub UserForm_Initialize()
code_frmMain.testRange3
End Sub
My sample program code:
' This subroutine will search column B for the selected value
Sub DisplayNext()
Dim searchTerm As String
Dim lastRow As Long
Dim i As Integer
' clear frmMain.lstResults
frmMain.lstResults.Clear
For i = 0 To frmMain.lstPrefixes.ListCount - 1
If frmMain.lstPrefixes.Selected(i) = True Then
searchTerm = frmMain.lstPrefixes.List(i)
Exit For ' exits once selected item is found
End If
Next i
'Debug.Print searchTerm
With Sheets("Temp")
lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
End With
For i = 1 To lastRow
If InStr(Cells(i, 2).Value, searchTerm) Then
frmMain.lstResults.AddItem (Cells(i, 2).Value)
End If
Next i
End Sub
' increments value. input direction: 0 is down and 1 is up
Sub IncrementValue(direction As Integer)
Dim currentIndex As Integer
currentIndex = -1
For i = 0 To frmMain.lstPrefixes.ListCount - 1
If frmMain.lstPrefixes.Selected(i) = True Then
currentIndex = frmMain.lstPrefixes.ListIndex
Exit For ' exits once selected item is found
End If
Next i
' defaults to first item if none selected
If currentIndex = -1 Then
frmMain.lstPrefixes.Selected(0) = True
currentIndex = 0
End If
If direction = 0 Then
' prevents listIndex from being invalid
If currentIndex = 0 Then
frmMain.lstPrefixes.Selected(frmMain.lstPrefixes.ListCount - 1) = True
Else
frmMain.lstPrefixes.Selected(currentIndex - 1) = True
End If
Else
If currentIndex = frmMain.lstPrefixes.ListCount - 1 Then
frmMain.lstPrefixes.Selected(0) = True
Else
frmMain.lstPrefixes.Selected(currentIndex + 1) = True
End If
End If
End Sub
Note that I also added this to the bottom of your testRange3() to use that data that you had already gathered:
For i = 0 To UBound(MyArUniqVal)
frmMain.lstPrefixes.AddItem (MyArUniqVal(i))
Next i
Sample Data:
Running on user form:

Trying to create a specific loop in excel to get an output

bear with me on this question. I'm pretty sure it'll be easy for those who have knowledge in this field, but I do not know much about VBA or how to create loops in Excel to be creating this formula:
Please review the picture here
What I'm trying to construct is a loop that'll concatenate those numbers.
EX. I want to concatenate in this order A2,"-",B2; A3,"-",B2; A4,"-",B2.....A16,"-",B2
Once everything in A1- A16 is concatenated with B2, I want to move on to concatenating A1-A16 with B3.EX: A2,"-",B3; A3,"-",B3.....A16,"-",B3
I know this is possible because certain loops can be created to go through with this procedure, but I do not know VBA and am not sure if this is possible with just the pre-existing formulas in Excel. Thanks to anyone who helps.
From what you described, it's pretty simple nested loop. Below code will concatenate the way you wanted and store it to column C.
Sub MyConcat()
Const lColA As Long = 1
Const lColB As Long = 2
Const lColTxt As Long = 3 ' concatenated result in Column C
Dim oWS As Worksheet, sTxt As String
Dim lRowA As Long, lRowB As Long, lRowTxt As Long
Set oWS = ThisWorkbook.Worksheets("Sheet1") ' Change this to match yours
lRowA = 1
lRowTxt = 1
oWS.Columns(lColTxt).Clear ' remove previous data on Column C
Do Until IsEmpty(oWS.Cells(lRowA, lColA))
sTxt = ""
lRowB = 2
Do Until IsEmpty(oWS.Cells(lRowB, lColB))
sTxt = oWS.Cells(lRowA, lColA).Text & "-" & oWS.Cells(lRowB, lColB).Text
oWS.Cells(lRowTxt, lColTxt) = sTxt
lRowB = lRowB + 1
lRowTxt = lRowTxt + 1
Loop
lRowA = lRowA + 1
Loop
Set oWS = Nothing
End Sub
EDIT: This should fit in many situations of number of Parent SKUs.
Usable on your data in second image, including another set of "TuTi" and Parent SKUs of different length. Please try understand it, it will be a whole page of explanations.
Private Const lColA As Long = 1
Private Const lColB As Long = 2
Private Const lColTxt As Long = 3 ' concatenated result in Column C
Dim oWS As Worksheet, sGroup As String, lRowCurr As Long, lRowTxt As Long
Sub MyConcat()
Dim oRng As Range, lStopRow As Long
Set oWS = ThisWorkbook.Worksheets("Sheet1") ' Change this to match yours
lRowCurr = 1 ' Current Row index
lRowTxt = 1 ' Results from Row 1
sGroup = ""
With oWS
.Columns(lColTxt).Clear ' remove previous data on Column C
' Row of LastCell in current sheet + 1
lStopRow = .Cells.SpecialCells(xlLastCell).Row + 1
' Row of "Ctrl-Up" from LastCell Row at column A
lStopRow = .Cells(lStopRow, lColA).End(xlUp).Row + 1
' Start processing rows until until StopRow in column A
Do Until lRowCurr = lStopRow
Set oRng = .Cells(lRowCurr, lColA)
If IsGroupCell(oRng) Then
sGroup = oRng.Value ' Stores Group text
ElseIf IsParentSKU(oRng) Then
Call MyConcat2 ' Invoke the mix sub that writes the result in column C
End If
lRowCurr = lRowCurr + 1
Set oRng = Nothing
Loop
End With
Set oWS = Nothing
End Sub
Private Sub MyConcat2()
Dim sTxt As String, oRng As Range
Dim lRowA As Long, lRowB As Long
lRowA = lRowCurr + 1
Set oRng = oWS.Cells(lRowA, lColA)
' Stop mixing the values when it is a Group or Parent SKU row
Do Until IsGroupCell(oRng) Or IsParentSKU(oRng) Or IsEmpty(oRng)
sTxt = ""
lRowB = lRowCurr + 1
' Don't mix if it is a Parent SKU
Do Until IsParentSKU(oWS.Cells(lRowB, lColA)) Or IsEmpty(oWS.Cells(lRowB, lColB))
sTxt = oWS.Cells(lRowA, lColA).Text & "-" & oWS.Cells(lRowB, lColB).Text
oWS.Cells(lRowTxt, lColTxt) = sGroup & "-" & sTxt
lRowB = lRowB + 1
lRowTxt = lRowTxt + 1
Loop
lRowA = lRowA + 1
Set oRng = oWS.Cells(lRowA, lColA)
Loop
lRowCurr = lRowA - 1
Set oRng = Nothing
End Sub
Private Function IsGroupCell(oRng As Range) As Boolean
IsGroupCell = (Not IsNumeric(Left(oRng.Value, 1)) And IsEmpty(oRng.Offset(0, 1)))
End Function
Private Function IsParentSKU(oRng As Range) As Boolean
IsParentSKU = (IsNumeric(oRng.Value) And IsNumeric(oRng.Offset(0, 1).Value))
End Function

Resources