Sub FindValue()
Dim firstAddress As String
Dim Expense As String
Dim rRange As Range
Dim FirstrngFnd As Range
Dim x As Integer
Dim y As Integer
Dim z As Integer
Workbooks.Open FileName:= _
"D:\My Documents\Excel Files\AA Credit Card\1008.xlsx" 'A worksheet with several columns - Column G (Column 7) is a list of "expenses".
Workbooks.Open FileName:= _
"D:\My Documents\Excel Files\Credit Card Analysis.xlsx" 'A worksheet with "expenses" listed in random order in Column A (Column1)
Windows("Credit Card Analysis.xlsx").Activate
ActiveWindow.Panes(1).Activate
Expense = Application.InputBox("Select the required expense from Column A") 'Pick an "expense" from a list in Column A
'Open the first file in the first folder
Windows("1008.xlsx").Activate
ActiveWindow.Panes(1).Activate
'Establish the first and last rows in Column A (which contain a list of dates by increasing date): required to establish the search range
Set rRange = Range("A1", Cells(Rows.Count, 1).End(xlUp))
For Each rCell In rRange
If IsDate(rCell) Then
rCell(2, 1).Select
Exit For
End If
Next rCell
x = (ActiveCell.Row - 1) 'This finds the FIRST row in the file with a date in it
y = Cells(Rows.Count, 1).End(xlUp).Row 'This finds the LAST row in the file with a date in it
My_Workbook = ActiveWorkbook.Name 'Holds the current Workbook name
'Move over to the "analysis" column (G) (Column 7)
With Worksheets(1).Range(Cells(x, 7), Cells(y, 7))
Set FirstrngFnd = .Find(Expense, LookIn:=xlValues, LookAt:=xlPart) 'Finds the first occurrence of "expense"
If Not FirstrngFnd Is Nothing Then 'if the "expense" isn't listed then goto Line400
firstAddress = FirstrngFnd.Address
Do 'DO WHATEVER IS REQUIRED IN THIS SECTION: FROM "DO" TO "Set FirstrngFnd = .FindNext(FirstrngFnd)"
z = FirstrngFnd.Row
FirstrngFnd.Value = "Mike" 'IF YOU OMIT THIS LINE THEN ALL THE VALUES REMAIN AT "expense", SO THE PROGRAM JUST GOES ROUND (AND ROUND) AGAIN.
Set FirstrngFnd = .FindNext(FirstrngFnd)
Loop While Not FirstrngFnd Is Nothing
End If
End With
End Sub
If I remove the line "FirstrngFnd.Value = "Mike" then the values in Golumn G never change so when the program gets to the end of the file, it just goes round again.
How can I get it to recognise it's been through the file once, and to move on?
Please, modify:
Loop While Not FirstrngFnd Is Nothing
in this way:
Loop While Not FirstrngFnd Is Nothing And FirstrngFnd.Address <> firstAddress
Find in an Endless Loop
This covers the case when you are using the Find method (incl. FIndNext) to find all occurrences of a value in a range, but you don't want to change them and you also don't want to end up in an endless loop.
To simplify, the procedures use Sheet1 (CodeName) and the range "A1:A3" and "Yes" as the criteria.
Copy them into a new workbook.
Test first both procedures without values, then put in some "Yes" values (without the double quotes) in the cells. You can also change the range to e.g. A1:A10, to better understand.
The Code
Option Explicit
' To exit the endless loop, press and hold down the ESC key.
Sub testEndlessLoop()
Dim rng As Range
Dim cel As Range
Set rng = Sheet1.Range("A1:A3")
Set cel = rng.Find( _
What:="Yes", _
After:=rng(rng.Cells.Count), _
LookIn:=xlFormulas)
' Assuring that a cel containing "Yes" has been found. Once it has
' been found, find will always find an occurrence of "Yes" whether
' there is one, two or three occurrences in the range,
' which will result in an endless loop (Do Loop).
If Not cel Is Nothing Then
' A cell containing "Yes" has been found.
Do
MsgBox "Found ""Yes"" in cell '" & cel.Address & "'."
' Find the next occurrence of "Yes"
Set cel = rng.FindNext(cel)
' 'While Not cel Is Nothing' is redundant because 'cel' will
' never be 'Nothing' anyway, which is ensured previously with
' 'If Not cel Is Nothing Then'.
Loop While Not cel Is Nothing
Else
' A cell containing "Yes" has not been found.
MsgBox "No occurrences found."
End If
End Sub
Sub testWorkingLoop()
Dim rng As Range
Dim cel As Range
Dim FirstAddress As String
Set rng = Sheet1.Range("A1:A3")
Set cel = rng.Find( _
What:="Yes", _
After:=rng(rng.Cells.Count), _
LookIn:=xlFormulas)
If Not cel Is Nothing Then
' A cell containing "Yes" has been found.
' Write the range address of the first found occurrence to a variable.
FirstAddress = cel.Address
Do
' This is where you do stuff when "Yes" is found.
MsgBox "Found ""Yes"" in cell '" & cel.Address & "'."
' Most often you don't want to change the found cell,
' but you want to change another cell in the same row,
' e.g. write to the cell in column 'B'.
cel.Offset(0, 1).Value = "Found a ""Yes""."
' This is where you do stuff when "Yes" is found.
' Find the next occurrence of "Yes"
Set cel = rng.FindNext(cel)
' Again, 'cel' will never be 'Nothing'.
Loop While cel.Address <> FirstAddress
Else
' A cell containing "Yes" has not been found.
MsgBox "No occurrences found."
End If
End Sub
Related
I am trying to search for values listed in a column from multiple sheets in my excel workbook. If excel finds a match I would like it to return sheet names of the tabs that had the value.
Here is what i have done so far. I decided to start off by using one keyword to search multiple tabs, copy and paste the sheet name. The code below only paste the first resulting sheet name when there are other sheets containing the same keyword. I would like to know how i can pull the other sheet names that contain the same keyword.
I would also like to know how i can set up the keyword to use information in Column A of the Field List.
Sub FinalAppendVar()
Dim ws As Worksheet
Dim arr() As String
Keyword = "adj_veh_smart_tech_disc"
Totalsheets = Worksheets.Count
For i = 1 To Totalsheets
If Worksheets(i).Name <> "Main" Or InStr(1, Worksheets(i).Name, " Checks") Or Worksheets(i).Name
<>_ "Field Lists" Then
lastrow = Worksheets(i).Cells(Rows.Count, 4).End(xlUp).Row
For j = 2 To lastrow
If Worksheets(i).Cells(1, 3).Value = Keyword Then
Worksheets("Field Lists").Activate
lastrow = Worksheets("Field Lists").Cells(Rows.Count, 4).End(xlUp).Row
Worksheets("Field Lists").Cells(lastrow + 1, 5).Value = Worksheets(i).Name
Worksheets("Field Lists").Cells(lastrow + 2, 5).Value = Worksheets(i).Name
End If
Next
End If
Next
End Sub
The following code should work for what you described.
A couple feedback items:
Tabbing out loops and if statements significantly improves code readability
Never reuse variable names (i.e. lastrow), it makes it hard to read and can cause issues that are difficult to find later on
Follow all Next with the loop variable (i.e. Next i), this improves readability and helps you keep track of the ends of loops
.Activate and .Select are generally never required in vba, its better to be explicit in what you are referencing
Sub FinalAppendVar()
Dim searchSheet As Excel.Worksheet
Dim pasteSheet As Excel.Worksheet
Dim keyword As String
Dim lastSearchRow As Integer
Dim lastPasteRow As Integer
' set the worksheet to paste to
Set pasteSheet = ThisWorkbook.Worksheets("Field Lists")
' set keyword to look for
keyword = "adj_veh_smart_tech_disc" '<-- manual entry
'keyword = pasteSheet.Range("A1").Value '<-- use value in cell A1 on the defined pasteSheet
' loop through all sheets in the workbook
For i = 1 To ThisWorkbook.Worksheets.Count
' set the current worksheet we are looking at
Set searchSheet = ThisWorkbook.Worksheets(i)
' check if the current sheet is one we want to search in
If searchSheet.Name <> "Main" Or InStr(1, searchSheet.Name, " Checks") Or searchSheet.Name <> "Field Lists" Then
' current worksheet is one we want to search in
' find the last row of data in column D of the current sheet
lastSearchRow = searchSheet.Cells(1048576, 4).End(xlUp).Row
' loop through all rows of the current sheet, looking for the keyword
For j = 2 To lastSearchRow
If searchSheet.Cells(j, 3).Value = keyword Then
' found the keyword in row j of column C in the current sheet
' find the last row of column D in the paste sheet
'lastPasteRow = pasteSheet.Cells(1048576, 4).End(xlUp).Row
lastPasteRow = pasteSheet.Cells(1048576, 5).End(xlUp).Row '<-- update based on OPs comment
' paste the name of the current search sheet to the last empty cell in column E
pasteSheet.Cells(lastPasteRow + 1, 5).Value = searchSheet.Name
' not sure if the next line is needed, looks like it pastes again immediately below the previous
pasteSheet.Cells(lastPasteRow + 2, 5).Value = searchSheet.Name
' to save time consider exiting the search in the current sheet since the keyword was just found
' this will move to the next sheet immediately and not loop through the rest of the rows on the current
' search sheet. This may not align with the usecase so it is currently commented out.
'Exit For '<--uncomment this to move to the next sheet after finding the first instance of the keyword
Else
' the keyoword was not in row j of column C
' do nothing
End If
Next j
Else
' current sheet is one we don't want to search in
' do nothing
End If
Next i
End Sub
Please try this variant (Don't worry that the code is so long - the longer the programmer thought and the more wrote, the better the program works ... usually it is):
Option Explicit
Sub collectLinks()
Const LIST_SHEET_NAME As String = "Field Lists"
Dim wsTarget As Worksheet
Dim wsEach As Worksheet
Dim keywordCell As Range
Dim sKeyword As String
Dim linkCell As Range
Dim aFound As Range
Dim aCell As Range
On Error Resume Next
Set wsTarget = ActiveWorkbook.Worksheets(LIST_SHEET_NAME)
On Error GoTo 0
If wsTarget Is Nothing Then
MsgBox "'" & LIST_SHEET_NAME & "' not exists in active workbook", vbCritical, "Wrong book or sheet name"
Exit Sub
End If
Rem Clear all previous results (from column B to end of data)
wsTarget.UsedRange.Offset(0, 1).ClearContents
Rem Repeat for each cell of column A in UsedRange:
For Each keywordCell In Application.Intersect(wsTarget.UsedRange, wsTarget.Columns("A")) ' It can be changed to "D", "AZ" or any other column
sKeyword = keywordCell.Text
If Trim(sKeyword) <> vbNullString Then
Application.StatusBar = "Processed '" & sKeyword & "'"
Set linkCell = keywordCell
For Each wsEach In ActiveWorkbook.Worksheets
If wsEach.Name <> LIST_SHEET_NAME Then
Application.StatusBar = "Processed '" & sKeyword & "' Search in '" & wsEach.Name & "'"
Set aFound = FindAll(wsEach.UsedRange, sKeyword)
If Not aFound Is Nothing Then
For Each aCell In aFound
Set linkCell = linkCell.Offset(0, 1) ' Shift to rught, to the next column
linkCell.Formula2 = "=HYPERLINK(""#" & aCell.Address(False, False, xlA1, True) & """,""" & _
aCell.Worksheet.Name & " in cell " & aCell.Address(False, False, xlA1, False) & """)"
Next aCell
End If
End If
Next wsEach
End If
Next keywordCell
Application.StatusBar = False
Rem Column width
wsTarget.UsedRange.Columns.AutoFit
End Sub
Function FindAll(SearchRange As Range, FindWhat As Variant) As Range
Dim FoundCell As Range
Dim FirstFound As Range
Dim LastCell As Range
Dim ResultRange As Range
Dim Area As Range
Dim MaxRow As Long
Dim MaxCol As Long
For Each Area In SearchRange.Areas
With Area
If .Cells(.Cells.Count).Row > MaxRow Then
MaxRow = .Cells(.Cells.Count).Row
End If
If .Cells(.Cells.Count).Column > MaxCol Then
MaxCol = .Cells(.Cells.Count).Column
End If
End With
Next Area
Set LastCell = SearchRange.Worksheet.Cells(MaxRow, MaxCol)
Rem If your keyword can be a part of cell then change parameter xlWhole to xlPart:
Set FoundCell = SearchRange.Find(FindWhat, LastCell, xlValues, xlWhole, xlByRows)
If Not FoundCell Is Nothing Then
Set FirstFound = FoundCell
Do Until False ' Loop forever. We'll "Exit Do" when necessary.
If ResultRange Is Nothing Then
Set ResultRange = FoundCell
Else
Set ResultRange = Application.Union(ResultRange, FoundCell)
End If
Set FoundCell = SearchRange.FindNext(after:=FoundCell)
If (FoundCell Is Nothing) Then
Exit Do
End If
If (FoundCell.Address = FirstFound.Address) Then
Exit Do
End If
Loop
End If
Set FindAll = ResultRange
End Function
You can see how it works in this demo workbook - Create Links To Keywords.xlsm
EDIT By the way, the second part of this code, the FindAll() function, is a slightly shortened version of the Chip Pearson macro. Keep this link for yourself, there are many useful things to help you in future development.
I need a macro to add text to blank cells in Column A. The macro needs to skip cells that have text. The macro needs to stop looping at the end of the data set.
I am trying to use an If Else statement, but I think I'm on the wrong track. My current, non-working code is below. Thank you so much - I'm still new to VBA
Sub ElseIfi()
For i = 2 To 100
If Worksheets("RawPayrollDump").Cells(2, 1).Value = "" Then
Worksheets("RawPayrollDump").Cells(2, 1).Value = "Administration"
Else if(not(worksheets("RawPayrollDump").cells(2,1).value="")) then 'go to next cell
End If
Next
End Sub
To find the last row of data, use the End(xlUp) function.
Try this code. It replaces all empty cells in column A with Administration.
Sub ElseIfi()
Set ws = Worksheets("RawPayrollDump")
lastrow = ws.Cells(Rows.Count, 1).End(xlUp).Row ' last data row
For i = 2 To lastrow ' all rows until last data row
If ws.Cells(i, 1).Value = "" Then ' column A, check if blank
ws.Cells(i, 1).Value = "Administration" ' set text
End If
Next
End Sub
There is no need to loop. Please try this code.
Sub FillBlanks()
Dim Rng As Range
With Worksheets("RawPayrollDump")
Set Rng = Range(.Cells(2, "A"), .Cells(.Rows.Count, "A").End(xlUp))
End With
On Error Resume Next
Set Rng = Rng.SpecialCells(xlCellTypeBlanks)
If Err Then
MsgBox "There are no blank cells" & vbCr & _
"in the specified range.", _
vbInformation, "Range " & Rng.Address(0, 0)
Else
Rng.Value = "Administration"
End If
End Sub
Replace Blanks feat. CurrentRegion
Range.CurrentRegion
Since OP asked for "... stop looping at the end of the data set. ",
I've written this CurrentRegion version.
As I understand it, the end of the data set doesn't mean that there
cannot be blank cells below the last cell containing data in column
A.
Use the 1st Sub to test the 2nd, the main Sub (replaceBlanks).
Adjust the constants including the workbook (in the 1st Sub) to fit your needs.
Criteria is declared as Variant to allow other data types not just strings.
The Code
Option Explicit
Sub testReplaceBlanks()
Const wsName As String = "RawPayrollDump"
Const FirstCellAddress As String = "A2"
Const Criteria As Variant = "Administration"
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
replaceBlanks ws, FirstCellAddress, Criteria
End Sub
Sub replaceBlanks(Sheet As Worksheet, _
FirstCellAddress As String, _
Criteria As Variant)
' Define column range.
Dim ColumnRange As Range
Set ColumnRange = Intersect(Sheet.Range(FirstCellAddress).CurrentRegion, _
Sheet.Columns(Sheet.Range(FirstCellAddress) _
.Column))
' To remove the possibly included cells above the first cell:
Set ColumnRange = Sheet.Range(Range(FirstCellAddress), _
ColumnRange.Cells(ColumnRange.Cells.Count))
' Note that you can also use the addresses instead of the cell range
' objects in the previous line...
'Set ColumnRange = sheet.Range(FirstCellAddress, _
ColumnRange.Cells(ColumnRange.Cells.Count) _
.Address)
' or a mixture of them.
' Write values from column range to array.
Dim Data As Variant
If ColumnRange.Cells.Count > 1 Then
Data = ColumnRange.Value
Else
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = ColumnRange.Value
End If
' Modify array.
Dim i As Long, k As Long
For i = 1 To UBound(Data)
If IsEmpty(Data(i, 1)) Then Data(i, 1) = Criteria: k = k + 1
Next i
' Write modified array to column range.
' The following line is used when only the first cell is known...
'Sheet.Range(FirstCellAddress).Resize(UBound(Data)).Value = Data
' ...but since the range is known and is the same size as the array,
' the following will do:
ColumnRange.Value = Data
' Inform user.
If k > 0 Then GoSub Success Else GoSub Fail
Exit Sub
' Subroutines
Success:
MsgBox "Wrote '" & Criteria & "' to " & k & " previously " _
& "empty cell(s) in range '" & ColumnRange.Address & "'.", _
vbInformation, "Success"
Return
Fail:
MsgBox "No empty cells in range '" & ColumnRange.Address & "'.", _
vbExclamation, "Nothing Written"
Return
End Sub
I am searching a column for cell that contains text and does not contain the word "cat" in the first 6 characters (needs to be case insensitive). This will then cut that entire row to another sheet. Cannot get the code to run without compile errors. the below code is before i try to change it. I do not know how to code it to look at the first 6 characters.
tried instr & iserror but i think my existing code just needs a small alteration which escapes me.
Sub CATDEFECTS()
UsdRws = Range("C" & Rows.Count).End(xlUp).Row
For i = UsdRws To 2 Step -1
If Range("C" & i).Value Like "<>""" And Range("c" & i).Value Like "CAT" Then
Rows(i).Cut Sheets("AWP DEFECTS").Range("A" & rows.Count).End(xlUp).Offset(1)
Rows(i).Delete
End If
Next i
End Sub
Regardless of how you decide to implement the macro, your test to see if a cell is blank is entirely redundant. You can just test if the cell meets your CAT criteria. If it does, it is definitely not blank so no need to test it.
Method 1
You can look at the first 6 characters with LEFT(Range, 6)
If Left(Range("C" & i), 6) Like "*CAT*" Then
This needs Option Compare to work (Thanks #Comintern)
Method 2
I would prefer this method. Its explicit and does not delete or shift anything inside the loop so your action statements are greatly minimized.
Sub Cat()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1") '<--UPDATE
Dim ps As Worksheet: Set ps = ThisWorkbook.Sheets("AWP DEFECTS")
Dim LR As Long, DeleteMe As Range, i As Long
LR = ws.Range("C" & ws.Rows.Count).End(xlUp).Row
For i = 2 To LR
If InStr(Left(ws.Range("C" & i), 6), "CAT") Then
If Not DeleteMe Is Nothing Then
Set DeleteMe = Union(DeleteMe, ws.Range("C" & i))
Else
Set DeleteMe = ws.Range("C" & i)
End If
End If
Next i
Application.ScreenUpdating = False
If Not DeleteMe Is Nothing Then
LR = ps.Range("A" & ps.Rows.Count).End(xlUp).Row
DeleteMe.EntireRow.Copy ps.Range("A" & LR)
DeleteMe.EntireRow.Delete
End If
Application.ScreenUpdating = True
End Sub
If cat is within the first 6 characters then InStr will report its position being less than 5.
Sub CATDEFECTS()
dim UsdRws as long, pos as long
UsdRws = Range("C" & Rows.Count).End(xlUp).Row
For i = UsdRws To 2 Step -1
pos =instr(1, cells(i, "C").value2, "cat", vbtextcompare)
If pos > 0 and pos < 5 Then
Rows(i).Cut Sheets("AWP DEFECTS").Range("A" & rows.Count).End(xlUp).Offset(1)
Rows(i).Delete
End If
Next i
End Sub
Criteria Backup (Hide/Delete)
To enable the deletion of the rows in the Source Worksheet you have to set cDEL to True in the constants section. Adjust the other constants to fit you needs.
The Code
Option Explicit
'Option Compare Text
Sub CATDEFECTS()
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
On Error GoTo ProcedureExit
' Source Constants
Const cSource As Variant = "Sheet1" ' Worksheet Name/Index
Const cCol As Variant = "C" ' Search Column Letter/Number
Const cFirstR As Long = 2 ' First Row Number
Const cChars As Long = 6 ' Number of Chars
Const cSearch As String = "CAT" ' Search String
' Target Constants
Const cTarget As Variant = "AWP DEFECTS" ' Worksheet Name/Index
Const cColTgt As Variant = "A" ' Column Letter/Number
Const cFirstRTgt As Long = 2 ' First Row Number
Const cDEL As Boolean = False ' Enable Delete (True)
' Variables
Dim rngH As Range ' Help Range
Dim rngU As Range ' Union Range
Dim vntS As Variant ' Source Array
Dim i As Long ' Source Range Row Counter
' The Criteria
' When the first "cChars" characters do not contain the case-INsensitive
' string "cSearch", the criteria is met.
' Source Worksheet
With ThisWorkbook.Worksheets(cSource)
' Calculate Last Cell in Search Column using the Find method and
' assign it to Help (Cell) Range.
Set rngH = .Columns(cCol).Find("*", , xlFormulas, _
xlWhole, xlByColumns, xlPrevious)
' Calculate Source Column Range from Help (Cell) Range.
If Not rngH Is Nothing Then ' Last Cell was found.
' Calculate Source Column Range and assign it to
' Help (Column) Range using the Resize method.
Set rngH = .Cells(cFirstR, cCol).Resize(rngH.Row - cFirstR + 1)
' Copy Help (Column) Range into 2D 1-based 1-column Source Array.
vntS = rngH
' Show hidden rows to prevent the resulting rows (the rows to be
' hidden or deleted) to appear hidden in Target Worksheet.
rngH.EntireRow.Hidden = False
Else ' Last Cell was NOT found (unlikely).
MsgBox "Empty Column '" & cCol & "'."
GoTo ProcedureExit
End If
' Loop through rows of Source Array.
For i = 1 To UBound(vntS)
' Check if current Source Array value doesn't meet Criteria.
If InStr(1, Left(vntS(i, 1), cChars), cSearch, vbTextCompare) = 0 _
Then ' "vbUseCompareOption" if "Option Compare Text"
' Note: To use the Like operator instead of the InStr function
' you have to use (uncomment) "Option Compare Text" at the beginning
' of the module for a case-INsensitive search and then outcomment
' the previous and uncomment the following line.
' If Not Left(vntS(i, 1), cChars) Like "*" & cSearch & "*" Then
Set rngH = .Cells(i + cFirstR - 1, cCol)
If Not rngU Is Nothing Then
' Union Range contains at least one range.
Set rngU = Union(rngU, rngH)
Else
' Union Range does NOT contain a range (only first time).
Set rngU = rngH
End If
End If
Next
End With
' Target Worksheet
If Not rngU Is Nothing Then ' Union Range contains at least one range.
With ThisWorkbook.Worksheets(cTarget)
' Calculate Last Cell in Search Column using the Find method and
' assign it to Help Range.
Set rngH = .Columns(cColTgt).Find("*", , xlFormulas, _
xlWhole, xlByColumns, xlPrevious)
' Calculate Last Cell from Help Range, but in column 1 ("A").
If Not rngH Is Nothing Then ' Last Cell was found.
Set rngH = .Cells(rngH.Row + 1, 1)
Else ' Last Cell was NOT found.
Set rngH = .Cells(cFirstRTgt - 1, 1)
End If
' Copy the entire Union Range to Target Worksheet starting from
' Help Range Row + 1 i.e. the first empty row (in one go).
' Note that you cannot Cut/Paste on multiple selections.
rngU.EntireRow.Copy rngH
End With
' Hide or delete the transferred rows (in one go).
If cDEL Then ' Set the constant cDEL to True to enable Delete.
rngU.EntireRow.Delete
Else ' While testing the code it is better to use Hidden.
rngU.EntireRow.Hidden = True
End If
End If
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Remarks
The use of the array did not speed up considerably.
The InStr function was a few milliseconds faster than the Like operator in my data set.
Calculating the Real Used Range and copying it into a Source Array
and then writing the data that meets the criteria from Source Array
to a Target Array and copying the Target Array to the Target
Worksheet, might be faster, and/but would additionally copy the data without formulas or formatting.
I am trying to write a macro that hides the row if the cell value equals the next visible cell in that column and loops through the whole column. I have read that SpecialCells(xlCellTypeVisible) only works up to 8192 cells and my spreadsheet has 15,000 rows.
I have tried something like this but want to restrict it to only visible cells
Sub Test()
For i = 7 To 15258
If Range("P" & i).Value = Range("P" & i + 1).Value Then
Rows(i).Hidden = True
End If
Next i
End Sub
I have tried to search for a solution but haven't been able to find one yet.
Thanks!
I'd be surprised if this couldn't be optimized just a little bit, but it will work for what you are needing.
You can follow the comments within the code itself to kind of get a sense of what it's doing, but in a nutshell, you are using a For...Next statement to loop through your visible cells. For each visible cell, you will search for the next visible cell and then check to see if that matches. If it does, you add that cell to a special range that tracks all the rows to hide at the end of the code, then hide it.
Sub Test()
Dim ws As Worksheet, lookupRng As Range, rng As Range, lstRow As Long
Set ws = ThisWorkbook.Worksheets(1)
lstRow = 15258
Set lookupRng = ws.Range("P7:P" & lstRow)
Dim rngToHide As Range, i As Long
For Each rng In lookupRng.SpecialCells(xlCellTypeVisible)
Application.StatusBar = "Checking row " & rng.Row & " for matches."
For i = rng.Row + 1 To lstRow 'Loop through rows after rng
If Not ws.Rows(i).Hidden Then 'Check if row is hidden
If rng.Value = ws.Cells(i, "P") Then 'check if the non-hidden row matches
If rngToHide Is Nothing Then 'Add to special range to hide cells
Set rngToHide = ws.Cells(i, "P")
Else
Set rngToHide = Union(rngToHide, ws.Cells(i, "P"))
End If
End If
Exit For 'Exit the second For statement
End If
Next i
Next rng
Application.StatusBar = "Hiding duplicate rows"
If Not rngToHide Is Nothing Then rngToHide.EntireRow.Hidden = True
Application.StatusBar = False
End Sub
I need to find out the first cell and the last cell of a vertically merged cell..
Let's say I merge Cells B2 down to B50.
How can I get in VBA the start cell(=B2) and the end cell(=B50)?
Sub MergedAreaStartAndEnd()
Dim rng As Range
Dim rngStart As Range
Dim rngEnd As Range
Set rng = Range("B2")
If rng.MergeCells Then
Set rng = rng.MergeArea
Set rngStart = rng.Cells(1, 1)
Set rngEnd = rng.Cells(rng.Rows.Count, rng.Columns.Count)
MsgBox "First Cell " & rngStart.Address & vbNewLine & "Last Cell " & rngEnd.Address
Else
MsgBox "Not merged area"
End If
End Sub
Below macro goes through all sheets in a workbook and finds merged cells, unmerge them and put original value to all merged cells.
This is frequently needed for DB applications, so I wanted to share with you.
Sub BirlesenHucreleriAyirDegerleriGeriYaz()
Dim Hucre As Range
Dim Aralik
Dim icerik
Dim mySheet As Worksheet
For Each mySheet In Worksheets
mySheet.Activate
MsgBox mySheet.Name & “ yapılacak…”
For Each Hucre In mySheet.UsedRange
If Hucre.MergeCells Then
Hucre.Orientation = xlHorizontal
Aralik = Hucre.MergeArea.Address
icerik = Hucre
Hucre.MergeCells = False
Range(Aralik) = icerik
End If
Next
MsgBox mySheet.Name & " Bitti!!"
Next mySheet
End Sub
Suppose you merged B2 down to B50.
Then, start cell address will be:
MsgBox Range("B2").MergeArea.Cells(1, 1).Address
End cell address will be:
With Range("B2").MergeArea
MsgBox .Cells(.Rows.Count, .Columns.Count).Address
End With
You can put address of any cell of merged area in place of B2 in above code.
Well, assuming you know the address of one of the cells in the merged range, you could just select the offset from that range and get the row/column:
Sub GetMergedRows()
Range("A7").Select 'this assumes you know at least one cell in a merged range.
ActiveCell.Offset(-1, 0).Select
iStartRow = ActiveCell.Row + 1
Range("A7").Select
ActiveCell.Offset(1, 0).Select
iEndRow = ActiveCell.Row - 1
MsgBox iStartRow & ":" & iEndRow
End Sub
The code above will throw errors if the offset row cannot be selected (i.e. if the merged rows are A1 through whatever) so you will want to add error handling that tells the code if it can't offset up, the top rows must be 1 and if it can't go down, the bottom row must be 65,536. This code is also just one dimensional so you might want to add the x-axis as well.
If you want the cell references as strings, you can use something like this, where Location, StartCell, and EndCell are string variables.
Location = Selection.Address(False, False)
Colon = InStr(Location, ":")
If Colon <> 0 Then
StartCell = Left(Location, Colon - 1)
EndCell = Mid(Location, Colon + 1)
End If
If you want to set them as ranges, you could add this, where StartRange and EndRange are Range objects.
set StartRange = Range(StartCell)
set EndRange = Range (EndCell)
If you intend to loop through the merged cells, try this.
Sub LoopThroughMergedArea()
Dim rng As Range, c As Range
Set rng = [F5]
For Each c In rng.MergeArea
'Your code goes here
Debug.Print c.Address'<-Sample code
Next c
End Sub