array references in a vba loop? - arrays

OK... second attempt at posting this. Trying to make my question make sense please be patient with me, I am new at all of this.
The code below words PERFECTLY. It reads from the Range in column A and give me a string of all the cells where the searched numbers occur.
My only issue is its obviously not condensed at all. I have tried loops and arrays for the references but i can't seem to get it work. i would prefer something along the lines of reference(1-10) in stead of ref1, ref2, ref3. i had them all ref(i) with a loop around the whole macro but i ended up getting no results and an endless loop. any help to make this more streamlined would be appreciated. i have the full code if that will help anyone. I only put three iterations of the code it simply continues on with more references. up to 9
Option Explicit
Sub FindAircraftHourly()
Dim Findarray() As Integer 'the array to put numbers in
Dim count1, count2, count3, count4, count5, count6, count7, count8, count9 As Integer 'this will count the number of records you will need for your array
Dim n1, n2, n3, n4, n5, n6, n7, n8, n9 As Integer 'this will be what gets you through your For loops
Dim Searchvalue1, Searchvalue2, Searchvalue3, Searchvalue4, Searchvalue5, Searchvalue6, Searchvalue7, Searchvalue8, Searchvalue9 As Integer 'this is the number to look for
Dim Foundstring1, Foundstring2, foundstring3, foundstring4, foundstring5, foundstring6, foundstring7, Foundstring8, Foundstring9 As String 'this will build where the numbers are found
Dim k1 As Integer
Dim data As Worksheet
Set data = ThisWorkbook.Sheets("data")
If Cells(1, 3) <> "" Then 'test to make sure it has something to look for
Searchvalue1 = Cells(1, 3) * 1 'E18 is the search value for the function this converts it to number and not text
Else
MsgBox "No input found!", vbCritical 'if blank, give a warning and quit
GoTo the_end
End If
k1 = data.Range("A5000").End(xlUp).Row
count1 = 0 'always initialize your variables
For n1 = 1 To k1 'count through all the rows to find the end of the list
If Cells(n1, 1) <> "" Then 'is the cell blank or not?
count1 = count1 + 1 'if not blank, then increase the count
Else
Exit For 'if it's blank, stop looking and counting
End If
Next n1
ReDim Findarray1(0 To count1 - 1) As Integer 'array indexes start at 0, so subtract 1 to get the right number of elements
For n1 = 0 To UBound(Findarray1) 'go from the first element to the last of the array
Findarray1(n1) = Cells(n1 + 1, 1) 'load the array change the last number in the cells box to change where to start looking
Next n1
For n1 = 0 To UBound(Findarray1)
If Findarray1(n1) = Searchvalue1 Then
If Foundstring1 = "" Then 'if this is the first time found, create the string
Foundstring1 = n1 + 1
Else
Foundstring1 = Foundstring1 & ", " & n1 + 1 'if this is the second time the number is found, add a comma and continue to build the string.
End If
End If
Next n1
If Foundstring1 = "" Then
MsgBox "Not found", vbCritical 'msgbox nothing was found
Else
Cells(1, 4) = Foundstring1 'output the string to a certain cell
End If
'-----------------------------------------------------------------------
If Cells(2, 3) <> "" Then 'test to make sure it has something to look for
Searchvalue2 = Cells(2, 3) * 1 'E18 is the search value for the function this converts it to number and not text
Else
MsgBox "No input found!", vbCritical 'if blank, give a warning and quit
GoTo the_end
End If
k1 = data.Range("A5000").End(xlUp).Row
count2 = 0 'always initialize your variables
For n2 = 1 To k1 'count through all the rows to find the end of the list
If Cells(n2, 1) <> "" Then 'is the cell blank or not?
count2 = count2 + 1 'if not blank, then increase the count
Else
Exit For 'if it's blank, stop looking and counting
End If
Next n2
ReDim FindArray2(0 To count2 - 1) As Integer 'array indexes start at 0, so subtract 1 to get the right number of elements
For n2 = 0 To UBound(FindArray2) 'go from the first element to the last of the array
FindArray2(n2) = Cells(n2 + 1, 1) 'load the array change the last number in the cells box to change where to start looking
Next n2
For n2 = 0 To UBound(FindArray2)
If FindArray2(n2) = Searchvalue2 Then
If Foundstring2 = "" Then 'if this is the first time found, create the string
Foundstring2 = n2 + 1
Else
Foundstring2 = Foundstring2 & ", " & n2 + 1 'if this is the second time the number is found, add a comma and continue to build the string.
End If
End If
Next n2
If Foundstring2 = "" Then
MsgBox "Not found", vbCritical 'msgbox nothing was found
Else
Cells(2, 4) = Foundstring2 'output the string to a certain cell
End If
'-----------------------------------------------------------
If Cells(3, 3) <> "" Then 'test to make sure it has something to look for
Searchvalue3 = Cells(3, 3) * 1 'E18 is the search value for the function this converts it to number and not text
Else
MsgBox "No input found!", vbCritical 'if blank, give a warning and quit
GoTo the_end
End If
k1 = data.Range("A5000").End(xlUp).Row
count3 = 0 'always initialize your variables
For n3 = 1 To k1 'count through all the rows to find the end of the list
If Cells(n3, 1) <> "" Then 'is the cell blank or not?
count3 = count3 + 1 'if not blank, then increase the count
Else
Exit For 'if it's blank, stop looking and counting
End If
Next n3
ReDim FindArray3(0 To count3 - 1) As Integer 'array indexes start at 0, so subtract 1 to get the right number of elements
For n3 = 0 To UBound(FindArray3) 'go from the first element to the last of the array
FindArray3(n3) = Cells(n3 + 1, 1) 'load the array change the last number in the cells box to change where to start looking
Next n3
For n3 = 0 To UBound(FindArray3)
If FindArray3(n3) = Searchvalue3 Then
If foundstring3 = "" Then 'if this is the first time found, create the string
foundstring3 = n3 + 1
Else
foundstring3 = foundstring3 & ", " & n3 + 1 'if this is the second time the number is found, add a comma and continue to build the string.
End If
End If
Next n3
If foundstring3 = "" Then
MsgBox "Not found", vbCritical 'msgbox nothing was found
Else
Cells(3, 4) = foundstring3 'output the string to a certain cell
End If
the_end: 'end marker
End Sub

I'm not sure what your data looks like, but I would suggest a few things:
'finds the absolute last row with data in it // ignores empty cells
k1 = data.UsedRange.Rows.Count
count1 = 0
For n1 = 1 To k1
'here's a built-in function to help you check empty cells.
'if you're going to reference ranges, then you need to define on which worksheet
'you're referencing the ranges from. if you work with multiple worksheets, you
'can't simply use Cells(x,y); you need to use worksheet.Cells(x,y).
'If you want to access a cell's value, then you use the .Value property.
If Not IsEmpty(data.Cells(n1, 1).Value) Then
count1 = count1 + 1
Else
Exit For
End If
Next n1
I highly suggest using Collections instead of arrays because, for me, they're far easier to deal-with than arrays. They also start their indices at 1, so if you have a collection of 10 items, you count 1 through 10 instead of 0 through 9.
Defining and setting Collections:
Dim FindArray As New Collection
Or
Dim FindArray As Collection
Set FindArray = New Collection
Iterating over a collection:
Dim v As Variant
Dim sample As Double
For Each v In FindArray
'i personally like to recast all variant types to
'the intended data type
sample = v
'do your thing here
Next
Or
For i = 0 To FindArray.Count
'do your other thing here
Next
Here's my attempt at making your code cleaner; hope it helps!
PS: All my castings are based on assumptions, so change them to whatever type you were intending.
Main:
Dim wb As Workbook
Dim data As Worksheet
Dim n As Long
Dim SearchValue As Double
Dim Found As String
Dim output As New Collection
Dim FindArray As Collection
Dim count As Long
Dim k As Long
Dim i As Long
Dim j As Long
Set wb = Excel.Application.ThisWorkbook
Set data = wb.Worksheets("data")
k = data.UsedRange.Rows.Count
'find the last row before an empty record appears
For n = 1 To k
If Not IsEmpty(data.Cells(n, 1).Value) Then
count = n
ElseIf IsEmpty(data.Cells(n ,1).Value) Then
Exit For
End If
Next
'do the thing 9 times
For n = 1 To 9
If Not IsEmpty(data.Cells(n, 3).Value) Then
'cast the cell value to double and assign it to searchvalue
SearchValue = CDbl(data.Cells(n, 3).Value)
'i prefer to be extremely specific, but that's just me
Elseif IsEmpty(data.Cells(n, 3).Value) Then
MsgBox "No input found!", vbCritical
GoTo the_end
End If
'reset findarray to an empty collection
Set FindArray = New Collection
For i = 1 To count
'load your collection with the sample values casted to doubles
FindArray.Add CDbl(data.Cells(i, 1).Value)
Next
j = 1
Found = ""
For i = 1 To FindArray.Count
If FindArray(i) = SearchValue Then
If j = 1 Then
Found = i
ElseIf j > 1 Then
'im not sure what you're trying to accomplish with this line,
'so im just going to leave it as is and adjust it for my solution
'so it gives you the same output
Found = n & ", " & i
End If
j = j + 1
End If
Next
If Found = "" Then
MsgBox "Not found", vbCritical
Else
output.Add Found
End If
'i like to always just set the collection to nothing before a new iteration
Set FindArray = Nothing
Next
'dump all results in column 4
For i = 1 To output.Count
data.Cells(i, 4).Value = output(i)
Next
'garbage collection
Set FindArray = Nothing
Set output = Nothing
Set wb = Nothing
Set data = Nothing
the_end:
Exit Sub
I'm not entirely sure if this attempt at a solution will work for you, but I hope it's helpful in any way. Good luck!

Col A populated with values, Col C populated with search values to be found in Col A, Col D is where the row numbers of each value in Col C can be found.
The number of values in Col A and Col C are essentially limited only by the number of rows in your spreadsheet with no changes required to the code.
If you have not added a Dictionary reference, click on [Tools] at the top of the screen, click on [References...], scroll way down until you find [Microsoft Scripting Runtime], click the check mark next to that, then click [OK].
Sub FindAircraftHourly()
Dim dnySource As New Scripting.Dictionary
Dim strCellValue As String
Dim i0 As Long
With ActiveSheet
For i0 = 2 To .Rows.Count 'This starts at row 2 and goes to the end of the list. I'm assuming row 1 has a heading of some sort.
If Not IsEmpty(.Cells(i0, 1)) Then 'If the cell in col A of that row has a value, add that value to the dictionary as the key and the row number(s) as the items(s).
strCellValue = .Cells(i0, 1).Value
dnySource(strCellValue) = dnySource(strCellValue) & i0 & ", "
Else
Exit For 'Once you hit an empty cell, stop building the dictionary.
End If
Next i0
For i0 = 2 To .Rows.Count
If Not IsEmpty(.Cells(i0, 3)) Then 'If the cell in col C has a value then look through the dictionary to see if you have a list of row numbers to display.
strCellValue = .Cells(i0, 3).Value
If dnySource.Exists(strCellValue) Then 'If there is a list of row numbers for that value, display them in col D.
.Cells(i0, 4).Value = Left$(dnySource(strCellValue), Len(dnySource(strCellValue)) - 2) 'The - 2 here chops off the extra ", " at the end
Else 'Otherwise, indicate that the value in col C doesn't exist in col A.
.Cells(i0, 4).Value = "Value Not Found"
End If
Else
Exit For
End If
Next i0
End With
End Sub

Here's my attempt to solve your problem.
For questions just ask.
Option Explicit
Sub FindAircraftHourly()
Dim data As Worksheet
Dim searchrange As Range
Dim cell As Range
Dim findarray() As String
Dim searcharray() As Integer
Dim i As Integer
Set data = ThisWorkbook.Worksheets("data") 'the sheet your working on
Const maxinputvals As Integer = 3 'The number of search values you want to be looking for
ReDim searcharray(1 To maxinputvals) As Integer 'VBA doesn't need to start at 0
ReDim findarray(1 To maxinputvals) As String 'VBA doesn't need to start at 0
i = 0 'not needed but always nice to make sure
Do
i = i + 1 'add 1 to the count-var
If i > maxinputvals Then Exit Do 'if the count-var si bigger than max number of searchvalues quit the loop
If data.Cells(i, 3) <> "" Then 'check if cells is not empty
searcharray(i) = data.Cells(i, 3).Value 'safe the searchvalue
Else
MsgBox "Searchvalue #" & i & " is missing." 'exit the sub if a searchvalue is missing
Exit Sub
End If
Loop
Set searchrange = data.Range("A1:A5000")
For Each cell In searchrange 'go through the whole range
If cell.Value <> "" Then 'check is cell is empty
For i = 1 To maxinputvals 'if not, check if match with one of the searchvals
If cell.Value = searcharray(i) Then
findarray(i) = findarray(i) & ", " & cell.Row 'append your findarray string
End If
Next i
Else
Exit For 'if cell is empty exit the loop
End If
Next cell
For i = 1 To maxinputvals
findarray(i) = Mid(findarray(i), 3) 'delete the first ", "
data.Cells(i, 4) = findarray(i) 'output the findstring
Next i
End Sub

Related

Looping through Unique Data Set to Return Matching Value on a Different Sheet

I have searched for something similar to what I am asking, and unfortunately there is nothing close to what I am looking for.
I have a unique data set here on Sheet(2): The goal is to return the values in the highlighted blue columns if it matches the same "Item#" for the box selected in a dropdown list of the box names on Sheet(1). Please see Sheet(1) here: Sheet(1) Set-Up.
The Item#'s on Sheet(1) are located in B3:B12 on Sheet(1). - I've added also another list where I would like my code to run In the column next to this is a blank where the matching items in blue would post.
I am trying to use For Loops to accomplish this. I understand that the data set is weird, but I want to keep it like that for the mere challenge of it (and also because I have a larger data set similar and am just using this as a test run)... My code so far is as follows:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
' In order to run code on sheet without a button or enabling in a module
Set KeyCells = Range("A1")
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
Dim i, j As Long
Dim n As Long
Dim box As String
Set sh2 = ThisWorkbook.Sheets(2)
Set rn2 = sh2.UsedRange
box = Sheets(1).Cells.Range("A1")
Dim k1 As Long
k1 = rn2.Rows.Count + rn2.Row - 1
n = 0
For i = 1 To k1
If Sheets(2).Cells(1, i) = box Then
If n = 0 Then
Sheets(1).Cells(3, 3).Value = Sheets(2).Cells(i, 2)
n = n + 1
End If
ElseIf n > 0 Then
For j = 3 To n + 2
If Sheets(2).Cells(2, i).Value = Sheets(1).Cells(j, 2).Value Then
If Sheets(2).Cells(2, i).Value <> Sheets(1).Cells(j, 2).Value Then
x = x
Else
x = x + 1
End If
End If
Next
If x = 0 Then
Sheets(1).Cells(3 + n, 3).Value = Sheets(2).Cells(2, i).Value
n = n + 1
End If
End If
x = 0
Next
End If
End Sub
Please let me know what you experts think!
Edit 2; the macro finds Sheet1.Range("A1").Value in Sheet2 row 1. It then loops through each cell below the found value in Sheet2. It then finds each cells value in Sheet1. It will then copy the cells value in Sheet2 from the next cell to the right, and place the value in the cell in Sheet1 to the next cell to the right. It then loops down to the next cell in sheet2, and performs the same task, etc.
Private Sub Worksheet_Change(ByVal target As Range) 'Works
Dim fndTrgt As Range, fndCel As Range
If target.Address = "$A$1" Then
Set fndTrgt = Sheets("Sheet2").Rows(1).Find(target.Value)
If Not fndTrgt Is Nothing Then
For i = 1 To 5
Set fndCel = Sheets("Sheet1").Range("A2:D12").Find(fndTrgt.Offset(i).Value)
If Not fndCel Is Nothing Then
fndCel.Offset(, 1).Value = fndTrgt.Offset(i, 1).Value
End If
Next i
End If
End If
End Sub

VBA_Loop through empty rows in a range and delete all in a batch (one-line)

I have developed the code below which loops through usedrange , stores the row strings into an array of 20 elements, and then deletes the rows at the end, as I find this a lot quicker than a FOR or a DO loop given a large amount of rows. The code works, however, the issue lies with the fact that when the rows are written to the array element they are hard coded, and when the .DELETE method is used , the rows shift accordingly, so the numbers shift and not everything is deleted.
Is there a way to delete an array of ranges in one batch or one line, something like
Arr(drows(1 to 20)).delete
Or do I have to find an alternative?
'**** NEEDS RE-WORK --> ADJUST FOR ROWS WHEN DELETING ****'
Sub loop_it()
Dim i As Integer 'counter for array
Dim j As Integer 'counter # 2
Dim rD As Integer 'count deleted
Dim z As Integer
Dim dRows(20) As String 'will track the rows deleted1
Dim cRow As Range 'current row
Dim cSht As Worksheet
Dim uB As Integer
Set cSht = ActiveSheet 'activesheet
i = 1: j = 1: z = 1
On Error GoTo viewerr
For Each cRow In cSht.UsedRange.Rows
If WorksheetFunction.CountA(cRow.Cells) = 0 Then
If Len(dRows(i)) < 250 Then 'not to breach length
dRows(i) = dRows(i) & cRow.Row & ":" & cRow.Row & ","
rD = rD + 1 'increment # rows deleted
Else
dRows(i) = dRows(i) 'nothing
i = i + 1
End If
End If
Next cRow
uB = UBound(dRows) 'max array
For j = 1 To uB
If j <= i Then 'skip empty strings
dRows(j) = Left(dRows(j), Len(dRows(j)) - z) ' trim last comma off of string
cSht.Range(dRows(j)).Delete 'delete the rows
Else
Exit For
End If
Next j
'need to combine the text string into one array
MsgBox rD & Chr(32) & "rows deleted!", vbExclamation + vbOKOnly, "success!"
Erase dRows 'clear array
Exit Sub
viewerr:
MsgBox Err.Description & Space(2), vbCritical
Erase dRows
End Sub
This is what typically get proposed for this task here:
Sub Tester()
'...
'...
Dim rngDelete As Range
For Each cRow In cSht.UsedRange.Rows
If WorksheetFunction.CountA(cRow.Cells) = 0 Then
If rngDelete Is Nothing Then
Set rngDelete = cRow.EntireRow
Else
Set rngDelete = Application.Union(rngDelete, cRow.EntireRow)
End If
End If
Next cRow
'delete rows if any found
If Not rngDelete Is Nothing Then rngDelete.Delete
End Sub

Compare values of an Array in VBA and don't increment the array or add the value if it is a duplicate

I'm trying to loop through a spread sheet of non consecutive values and read/compare the value to the rest of the values previously read into the array before incrementing the array dimension and adding the value to the array. Ill try to demonstrate with a little example below.
i.e.
Sub ArrayCompare()
Dim Cntry() As String
ArrayDim = 5 'The array is dimensioned with another counter that is not pertinent to this question but typically not greater than 5 in 1 dimension
ReDim Cntry(ArrayDim)
Range("C1").Select
Dim Counter As Integer
Counter = 8 'In the real spread sheet the counter is dynamic, ive just put this in as an example
Do Until Counter = 0
ArrayCounter = 0 'This is used to compare the array values Cntry(C0)
Do Until ActiveCell.Value <> ""
If ActiveCell.Value = "" Then
ActiveCell.Offset(1, 0).Select
Else: End If
Loop
If Active.Value = Cntry(ArrayCounter - 1) Or ActiveCell.Value = Cntry(ArrayCounter - 2) Or ActiveCell.Value = Cntry(ArrayCounter - 3) Or ActiveCell.Value = Cntry(ArrayCounter - 4) Then 'this doesn't work because the array is not dimensioned to this size yet.
ActiveCell.Offset(1, 0).Select
Else
Cntry(ArrayDim) = ActiveCell.Value
ArrayDim = ArrayDim + 1
End If
Counter = Counter - 1
Loop
End Sub
Use Collections, they're are easier to work with. Here's example code where you loop through your collection of stored values and mark a flag if you find a duplicate. The same thing can be done with an array if you insist on using them
Sub Add_Value_If_Not_Duplicate()
Dim My_Stuff As New Collection
Dim End_of_Data, i, t As Integer
Dim Unique_Value As Boolean
End_of_Data = 13 'This will obviously be different for you
Unique_Value = True
Dim New_Value As Integer
'Loops through Column A of sheet 2 to demonstrate the approach
For i = 1 To End_of_Data 'Iterate through the data in your excel sheet
New_Value = Sheet2.Range("A" & i).Value 'Store the new value in a variable
If My_Stuff.Count > 0 Then 'If you have previously read values
'Looping through previously recorded values
For t = 1 To My_Stuff.Count
If My_Stuff(t) = New_Value Then
Unique_Value = False 'If value already exist mark a flag
Exit For
End If
Next
Else
'If you have no previously read values
End If
'Add if Unique
If Unique_Value = True Then 'If value isn't already listed then add it
My_Stuff.Add (New_Value)
End If
Unique_Value = True 'Reset your
Next
For i = 1 To My_Stuff.Count
Sheet2.Range("B" & i) = My_Stuff(i) 'Printing to demonstrate
Next
End Sub

How can I search for multiple values using multidimensional Array?

This code is now working to search multiple values in multiple sheets.
How can I fix it to support searching multiple values at the same time without having to write every one . For example, I want to put in column A all my search values, and then I click on search, and it should search and give the value for all of them at the same time. What should I change in the code to do this function?
Please see the code and the images.
Dim i, j, k, l, m, n, no_sheets As Variant
Dim key, cursor, sheetname As Variant
Dim flag As Variant
Dim sheet1_count, sheet1_row, row_count As Integer
Dim Arr() As Variant
sheet1_count = WorksheetFunction.CountA(ThisWorkbook.Worksheets("sheet1").Range("A:A"))
no_sheets = 3 ' Number of sheets
k = 2
sheet1_row = sheet1_count 'My start in result sheet
key = ThisWorkbook.Worksheets("sheet1").Range("A" & sheet1_count) ' The value that the user will put in searching sheet in column A
For i = 2 To no_sheets ' sheet2 then sheet3 then sheet4 then sheet5 ..etc
flag = False
sheetname = "Sheet" & i
row_count = WorksheetFunction.CountA(ThisWorkbook.Worksheets(sheetname).Range("A:A")) ' It's a counter that will contain the range of row A in each sheet
For j = 1 To row_count 'I'll start from row 1 until the last sheet
cursor = ThisWorkbook.Worksheets(sheetname).Range("A" & j) 'Searching in column A in each sheet (1st row - last row) and put the value in this variable
If key = cursor Then ' If the entering value in sheet1 equal the value that we have in current sheet, do the following
' Copying the data
flag = True ' The data found
ThisWorkbook.Worksheets("sheet1").Range("A" & sheet1_row) = ThisWorkbook.Worksheets(sheetname).Range("A" & j)
ThisWorkbook.Worksheets("sheet1").Range("B" & sheet1_row) = ThisWorkbook.Worksheets(sheetname).Range("B" & j)
ThisWorkbook.Worksheets("sheet1").Range("C" & sheet1_row) = ThisWorkbook.Worksheets(sheetname).Range("C" & j)
ThisWorkbook.Worksheets("sheet1").Range("D" & sheet1_row) = ThisWorkbook.Worksheets(sheetname).Range("D" & j)
ThisWorkbook.Worksheets("sheet1").Range("E" & sheet1_row) = ThisWorkbook.Worksheets(sheetname).Range("E" & j)
ThisWorkbook.Worksheets("sheet1").Range("F" & sheet1_row) = ThisWorkbook.Worksheets(sheetname).Range("F" & j)
sheet1_row = sheet1_row + 1
Else
End If
Next j 'Go to the next row
Next i 'Go to the next sheet
MsgBox "finished, Do another search..!"
If key <> cursor Then
flag = False ' If the value not found
ThisWorkbook.Worksheets("sheet1").Range("B" & sheet1_row) = "Not found"
ThisWorkbook.Worksheets("sheet1").Range("C" & sheet1_row) = "Not found"
ThisWorkbook.Worksheets("sheet1").Range("D" & sheet1_row) = "Not found"
ThisWorkbook.Worksheets("sheet1").Range("E" & sheet1_row) = "Not found"
End If
End Sub
Sub MatchUnMatch_Click()
Dim i, j, k, l, m, n As Integer
Dim ListA_count, ListB_count, ListC_count, ListD_count, ListE_count As Integer
Dim key, cursor As String
Dim flag As Boolean
ListA_count = WorksheetFunction.CountA(ThisWorkbook.Worksheets("MatchUnmatch").Range("A:A"))
ListB_count = WorksheetFunction.CountA(ThisWorkbook.Worksheets("MatchUnmatch").Range("B:B"))
'ListA_count = ThisWorkbook.Worksheets("MatchUnMatch").Range("A2").End(xlDown).Row
'MsgBox ListA_count & " " & ListB_count
'=======================================================================================================
'
'
' Matching Logic for List 'A' and List 'B'
'
'
'=======================================================================================================
k = 2
For i = 2 To ListA_count
key = ThisWorkbook.Worksheets("MatchUnMatch").Range("A" & i)
For j = 1 To ListB_count
cursor = ThisWorkbook.Worksheets("MatchUnMatch").Range("B" & j)
'MsgBox "Key=" & Key & " Cursor=" & cursor
If key = cursor Then
ThisWorkbook.Worksheets("MatchUnMatch").Range("C" & k) = key
k = k + 1
Exit For
End If
Next j
Next i
'=======================================================================================================
'
'
' List 'A' items not in List 'B'
'
'
'=======================================================================================================
ListC_count = WorksheetFunction.CountA(ThisWorkbook.Worksheets("MatchUnmatch").Range("C:C"))
k = 2
For i = 2 To ListA_count
key = ThisWorkbook.Worksheets("MatchUnMatch").Range("A" & i)
flag = False
For j = 1 To ListC_count
cursor = ThisWorkbook.Worksheets("MatchUnMatch").Range("C" & j)
If key = cursor Then
flag = True
Exit For
End If
Next j
If flag = False Then
ThisWorkbook.Worksheets("MatchUnMatch").Range("D" & k) = key
k = k + 1
End If
Next i
'=======================================================================================================
'
'
' List 'B' items not in List 'A'
'
'
'=======================================================================================================
k = 2
For i = 2 To ListB_count
key = ThisWorkbook.Worksheets("MatchUnMatch").Range("B" & i)
flag = False
For j = 1 To ListC_count
cursor = ThisWorkbook.Worksheets("MatchUnMatch").Range("C" & j)
If key = cursor Then
flag = True
Exit For
End If
Next j
If flag = False Then
ThisWorkbook.Worksheets("MatchUnMatch").Range("E" & k) = key
k = k + 1
End If
Next i
End sub
see the image please, to understand what I meanI want to put in row A in search sheet (first sheet) many numbers and then I want to click on search button one time only that should give me all the values at the same time.I don't want to click one search more than one time.
I want someone to fix it for me please. As soon as possible :(
(*) updated after OP's requested functionality to save data from previous runs and have numbers not found in "data" sheets marked as "NOT FOUND"
(**) updated after OP's request to handle a variable number of columns
(***) updated to fix FindItems() function to handle non contiguous cells range
(****) updated to fix iRow updating in sub Main()
(*****) updated to have items to be searched in sheets whose cell "A1" has same content as that of "base" sheets
(******) updated to have items to be searched in column A of all data sheets, whatever the header of that column
While I was doing my code, Cornel's already given you an answer which is ok
however should you ever want to manage:
any different number of "data" Sheets (i.e.: sheets to seek for item number in its column "A" and gather relevant data from adjacent columns)
multiple occurrences of a "number" in any "data" sheet
(*) functionality to save previous data already in "base" sheet resulting from previous runs
(*) functionality to mark "NOT FOUND" in "base" sheet when number not found on any "data" sheet
(**) functionality to handle a variable number of columns
then you may want to use the following code
Option Explicit
Sub main()
Dim items() As Variant, itemToFind As Variant
Dim itemsNumber As Long, previousDataNumber As Long, dataShtNumber As Long, iRow As Long, i As Long, j As Integer
Dim itemsSht As Worksheet, dataShts() As Worksheet
Dim rngToCopy As Range
Dim itemFound As Boolean
Dim columnsNumberToCopyAndPaste As Long
columnsNumberToCopyAndPaste = 7 '<== here you set the number of columns to be copied form "data" sheet and pasted in "base" sheet
Set itemsSht = ThisWorkbook.Worksheets("Sheet1") ' this is the "base" sheet you take "numbers" from its column A, starting at row 2
Call GetItems(itemsSht, items(), itemsNumber, previousDataNumber) ' gather all "numbers" to be searched for in "data" sheets
Call GetDataWorksheets(dataShts(), ThisWorkbook, "Sheet1", dataShtNumber) ' gather all "data" sheets
iRow = 1
For i = 1 To itemsNumber 'loop through "numbers"
itemToFind = items(i) ' "number" to be searched for in "data" sheets
itemFound = False
For j = 1 To dataShtNumber 'loop through "data" worksheets
Set rngToCopy = FindItems(dataShts(j), itemToFind, 1, columnsNumberToCopyAndPaste) ' get "data" sheet column 1 cells with "number" along with 'columnsNumberToCopyAndPaste-1' adjacents cells
If Not rngToCopy Is Nothing Then ' if found any occurrence of the "number" ...
rngToCopy.Copy itemsSht.Cells(1, 1).Offset(previousDataNumber + iRow) ' ... copy it and paste into "base" sheet
iRow = iRow + rngToCopy.Count / columnsNumberToCopyAndPaste 'update "base" sheet row offset to paste subsequent cells, if any
itemFound = True
End If
Next j
If Not itemFound Then 'if NOT found any occurrence of the "number" ...
itemsSht.Cells(1, 1).Offset(previousDataNumber + iRow).Value = itemToFind
itemsSht.Cells(1, 2).Offset(previousDataNumber + iRow).Resize(1, columnsNumberToCopyAndPaste - 1).Value = "NOT FOUND"
iRow = iRow + 1
End If
Next i
itemsSht.Columns.AutoFit
End Sub
Sub GetItems(itemsSht As Worksheet, items() As Variant, itemsNumber As Long, previousDataNumber As Long)
With itemsSht
previousDataNumber = .Cells(.Rows.Count, 2).End(xlUp).Row - 1
itemsNumber = .Cells(.Rows.Count, 1).End(xlUp).Row - 1 - previousDataNumber
ReDim items(1 To itemsNumber) As Variant
With .Cells(2 + previousDataNumber, 1).Resize(itemsNumber)
If itemsNumber = 1 Then
items(1) = .Value
Else
items = WorksheetFunction.Transpose(.Value)
End If
End With
End With
End Sub
Function FindItems(sht As Worksheet, itemToFind As Variant, columnToSearchFor As Long, columnsToCopy As Long) As Range
Dim cell As Range, unionRng As Range
Dim firstAddress As String
With sht.Columns(columnToSearchFor)
Set cell = .Find(What:=itemToFind, LookAt:=xlWhole)
If Not cell Is Nothing Then
firstAddress = cell.Address
Set unionRng = cell.Resize(, columnsToCopy)
Do
Set unionRng = Union(unionRng, cell.Resize(, columnsToCopy))
Set cell = .FindNext(cell)
Loop While Not cell Is Nothing And cell.Address <> firstAddress
Set FindItems = unionRng
End If
End With
End Function
Sub GetDataWorksheets(shts() As Worksheet, wb As Workbook, noShtName As String, nShts As Long)
Dim sht As Worksheet
For Each sht In wb.Worksheets
With sht
If .Name <> noShtName Then
nShts = nShts + 1
ReDim Preserve shts(1 To nShts) As Worksheet
Set shts(nShts) = sht
End If
End With
Next sht
End Sub
(*) Actually I added a previousDataNumber variable to track data already there at the time the routine runs
(**) in columnsNumberToCopyAndPaste = 5 you set the number of columns to be handled
I split it into a "main" sub and some other "helper" subs or function in order to have clear and more maintainable/changeable code.
this habit has always helped me much more than I could ever expect at my beginnings, when I was used to code looong subs
Now I fully understand the problem, I have edited my initial Script. Now it includes a FINDNEXT loop after the first FIND, this searches all the duplicate values on the sheet. This loops until FINDNEXT.cell.address is the same as FIND.cell.address. To search only in column "A" I changed sheets(i).cells to sheets(i).Range("A:A") in the Find function
Sub find_cells()
Dim find_cell As Range
Dim colection_items As Collection
Dim look_up_value As String
nb_rows = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row 'count the number of rows with data on sheet(1)
Set colection_items = New Collection
For j = 2 To nb_rows
colection_items.Add Sheets(1).Cells(j, 1).Value
Next j
counter_rows = 2 'the first row on sheet(2) where we start copying data from
For col = 1 To colection_items.Count
look_up_value = colection_items(col)
For i = 2 To ThisWorkbook.Sheets.Count
Sheets(i).Select
Set find_cell = Sheets(i).Range("A:A").Find(What:=look_up_value, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlRows, SearchDirection:=xlNext, MatchCase:=False)
If Not find_cell Is Nothing Then
Dim cell_adrs As String
cell_adrs = find_cell.Address 'record address of the first instance of the lookup value on the sheet (i)
Sheets(1).Cells(counter_rows, 1).Value = find_cell
Sheets(1).Cells(counter_rows, 2).Value = find_cell.Offset(0, 1) 'copies data from the cell to the left by one column
Sheets(1).Cells(counter_rows, 3).Value = find_cell.Offset(0, 2) 'copies data from the cell to the left by 2 columns
'etc
counter_rows = counter_rows + 1
Do
Set find_cell = Sheets(i).Range("A:A").FindNext(find_cell) 'we lookup the next instance on sheet (i)
If cell_adrs <> find_cell.Address Then 'if the next value found is different than the first value from sheet(i)
Sheets(1).Cells(counter_rows, 1).Value = find_cell
Sheets(1).Cells(counter_rows, 2).Value = find_cell.Offset(0, 1) 'copies data from the cell to the left by one column
Sheets(1).Cells(counter_rows, 3).Value = find_cell.Offset(0, 2) 'copies data from the cell to the left by 2 columns
counter_rows = counter_rows + 1
'etc
End If
Loop Until cell_adrs = find_cell.Address 'when all the values have been found and find_cell goes back to the first value
cell_adrs = Empty
End If
Next i
Next col
Sheets(1).Select
End Sub

Search a range using values from an array

I'm trying to write a small macro to search a range of values (provided by an array full of user input) and, for the most part, return their current row. This range is a printable toolsheet of which I only need to print pages that I make changes too, therefore I am attempting to simplify my job by inputting the tools that have been updated, and having the macro tell me what pages to print.
I tried to make that as understandable as possibly, but let me know if clarification is needed.
The problem arises when I try to search the sheet using 'Application.WorksheetFunction'
Sub FunWithArrays()
Dim ToolNumber()
Dim i As Integer
i = 1
ReDim ToolNumber(1)
Do
ToolNumber(i) = InputBox("Please type the name of the tool with proper capitalization.", "Tool Number")
ReDim Preserve ToolNumber(UBound(ToolNumber) + 1)
i = i + 1
Loop Until ToolNumber(i - 1) = "Done"
MsgBox "Thank you for inputting the tool numbers.", vbOKOnly, "Input Complete"
Dim j As Integer
Dim ToolNoRow As Range
Dim PageNo As Double
For j = 1 To (i - 2)
ToolNoRow = Application.WorksheetFunction.Range("A:A").Text.Find(what:=ToolNumber(j), LookIn:=xlValues, lookat:=xlWhole)
PageNo = Application.WorksheetFunction.RoundUp((ToolNoRow / 1), 0)
MsgBox "Please print page " & PageNo
Next
End Sub
Any and all help is appreciated! Thank you!
I am not sure that I fully understand how you macro works, since it looks like you want to return row numbers but you talk about returning page numbers.
However, if you want to return the range of a cell in which a given input is found (since you have declared ToolNoRow As Range) you would do this by:
Set ToolNoRow = Worksheets("Sheet1").Range("A:A").Text.Find(what:=ToolNumber(j), LookIn:=xlValues, lookat:=xlWhole)
Note that you would have to state in what sheet you want to perform the search.
If you instead want to return the row in which this input is found, you could declare ToolNoRow as Long and then use:
ToolNoRow = Worksheets("Sheet1").Range("A:A").Text.Find(what:=ToolNumber(j), LookIn:=xlValues, lookat:=xlWhole).Row
You should probably include some error handling in your code, because Excel would return an error message whenever a search item is not found.
Try this
You may need to mod the logic after you get the cell address
Dim ToolNumber()
Dim i As Integer
i = 1
ReDim ToolNumber(1)
Do
ToolNumber(i) = InputBox("Please type the name of the tool with proper capitalization.", "Tool Number")
ReDim Preserve ToolNumber(UBound(ToolNumber) + 1)
i = i + 1
Loop Until ToolNumber(i - 1) = "Done"
MsgBox "Thank you for inputting the tool numbers.", vbOKOnly, "Input Complete"
Dim j As Integer
Dim ToolNoRow As Range
Dim PageNo As Double
For j = 1 To (i - 2)
With Range("A:A")
Set ToolNoCell = Find(what:=ToolNumber(j), LookIn:=xlValues, lookat:=xlWhole)
If Not c Is Nothing Then
firstAddress = c.Address
Do
c.Value = 5
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
'Do whatever Here
PageNo = Application.WorksheetFunction.RoundUp((ToolNoRow / 1), 0)
MsgBox "Please print page " & PageNo
Next

Resources