Excel VBA - search, offset, replace - vba

I have a settings sheet with unique identifiers in column D and replacement values in column F. I need to:
loop through all serial numbers in sheet settingscolumn D
find the row with the same serial in sheet test column A
get the replacement value from settings column F
replace the data in column B on the test sheet, in the same row as the previously searched serial
sounds simple enough but I am getting a type mismatch error when defining the for and to statement with the code below.
Sub Replace_List()
Dim rList As Range, cell As Range, n As Long
Application.ScreenUpdating = False
With ThisWorkbook.Sheets("Settings")
Set rList = .Range("D4", .Range("D" & Rows.Count).End(xlUp))
End With
For Each cell In rList
For n = cell.Value To cell.Offset(0, 2).Value Step 1
ThisWorkbook.Sheets("test").Columns("B:B").Replace What:=n, _
Replacement:=cell.Offset(0, 2).Value, _
LookAt:=xlWhole
Next n
Next cell
Application.ScreenUpdating = True
MsgBox "Replaced all items from the list.", vbInformation, "Replacements Complete"
End Sub
Any pointers on what I am doing wrong here are appreciated.
Thanks,
A2k
EDIT
Screenshots below:
Settings - I am looking up the survey ID and want to replace the original date with the correct one

I believe you want to use Find to find each occurrence, and then replace the value using an Offset of the found location:
Sub Replace_List()
Dim rList As Range, cel As Range, n As Long
Dim fnd As Range
Dim fndFirst As String
Application.ScreenUpdating = False
With ThisWorkbook.Sheets("Settings")
Set rList = .Range("D4", .Range("D" & .Rows.Count).End(xlUp))
End With
For Each cel In rList
Set fnd = ThisWorkbook.Worksheets("test").Columns("A:A").Find(What:=cel.Value, LookAt:=xlWhole)
If Not fnd Is Nothing Then
fndFirst = fnd.Address
Do
fnd.Offset(0, 1).Value = cel.Offset(0, 2).Value
Set fnd = ThisWorkbook.Worksheets("test").Columns("A:A").FindNext(After:=fnd)
Loop While fnd.Address <> fndFirst
End If
Next
Application.ScreenUpdating = True
MsgBox "Replaced all items from the list.", vbInformation, "Replacements Complete"
End Sub

Related

Insert entire row based upon prompted cell value

All, I have the following code, but I need to know how to amend it. I need a prompt or message box that asks me, which value in column A to look for. It should the find the corresponding value in Sheet1 Column A, and copy the Data from Column A to AL over to sheet2.
Here's my code:
Sub MM1()
Dim Check As Range, r As Long, lastrow2 As Long, lastrow As Long
Application.ScreenUpdating = False
lastrow = Worksheets("Sheet1").UsedRange.Rows.Count
lastrow2 = Worksheets("Sheet2").UsedRange.Rows.Count
If lastrow2 = 1 Then lastrow2 = 0
For r = lastrow To 2 Step -1
If Range("E" & r).Value = "Yes" Then
Rows(r).Cut Destination:=Worksheets("Sheet2").Range("A" & lastrow2 + 1)
lastrow2 = lastrow2 + 1
Else:
End If
Next r
Application.ScreenUpdating = True
End Sub
Also, this is to be a subset of code which will search for the exact row to insert at.
You don't need to do a manual loop through the rows in sheet1, just use VBA's native Find function. Also You're currently not getting user input, that can be achieved with an InputBox.
See the comments for details about the code.
This example copies the data from the first match:
Sub MM1()
Dim lastrowsheet2 As Long
' Use last cell in UsedRange for its row number,
' if row 1,2,... aren't used, then UsedRange will be shorter than you expect!
With ThisWorkbook.Sheets("Sheet2").UsedRange
lastrowsheet2 = .Cells(.Cells.Count).Row
End With
' Get user input for a search term
Dim userinput As String
userinput = InputBox("Enter a value to search for.", "Column A Search")
' Search for value
Dim findrange As Range
Set findrange = ThisWorkbook.Sheets("Sheet1").Columns("A").Find(what:=userinput, lookat:=xlWhole, LookIn:=xlValues)
If findrange Is Nothing Then
MsgBox "No matching search results"
Else
lastrowsheet2 = lastrowsheet2 + 1
' Copy values in found row to sheet 2, in new last row
ThisWorkbook.Sheets("Sheet2").Range("A" & lastrowsheet2, "AL" & lastrowsheet2).Value _
= ThisWorkbook.Sheets("Sheet1").Range("A" & findrange.Row, "AL" & findrange.Row).Value
End If
End Sub
This example copies the data from the every match in the column:
Sub MM1()
' Speed improvements
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' Use last cell in UsedRange for its row number,
' if row 1,2,... aren't used, then UsedRange will be shorter than you expect!
Dim lastrowsheet2 As Long
With ThisWorkbook.Sheets("Sheet2").UsedRange
lastrowsheet2 = .Cells(.Cells.Count).Row
' If sheet is completely empty, make sure data will be inserted on row 1 not 2
If lastrowsheet2 = 1 And .Cells(1).Value = "" Then lastrowsheet2 = 0
End With
' Get user input for a search term
Dim userinput As String
userinput = InputBox("Enter a value to search for.", "Column A Search")
' Search for value
Dim findrange As Range
Dim firstaddress As String
Set findrange = ThisWorkbook.Sheets("Sheet1").Columns("A").Find(what:=userinput, lookat:=xlWhole, LookIn:=xlValues)
If findrange Is Nothing Then
MsgBox "No matching search results"
Else
firstaddress = findrange.Address
Do
lastrowsheet2 = lastrowsheet2 + 1
' Copy values in found row to sheet 2, in new last row
ThisWorkbook.Sheets("Sheet2").Range("A" & lastrowsheet2, "AL" & lastrowsheet2).Value _
= ThisWorkbook.Sheets("Sheet1").Range("A" & findrange.Row, "AL" & findrange.Row).Value
' Find next match
Set findrange = ThisWorkbook.Sheets("Sheet1").Columns("A").FindNext(findrange)
' Loop until the Find has wrapped back around, or value not found any more
Loop While Not findrange Is Nothing And findrange.Address <> firstaddress
End If
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub

Search Column Header Label Values

Is it possible to search row 1 (headers) for a value defined by a table from another sheet? I need "FName" to be a column or range of values as opposed to a single cell.
Here is a sample of what I was able to get working so far:
FName = Workbooks("IntChk.xlsm").Worksheets("Data").Range("B3")
Set rngFound = Worksheets("File").Rows(1).Find(What:=FName, LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False)
After identifying the search term from another workbook, you want to locate one or more occurrences in row 1 of this workbook (...?) and record the columns that correspond to the match(es).
Option Explicit
Sub get_em_all()
Dim fName As String, addr As String
Dim rng As Range, fnd As Range
'get search criteria
fName = Workbooks("IntChk.xlsm").Worksheets("Data").Range("B3")
With ThisWorkbook '<~~ different from IntChk.xlsm...?
With .Worksheets("File").Rows(1)
'perform first search
Set fnd = .Rows(1).Find(What:=fName, MatchCase:=False, _
LookIn:=xlValues, LookAt:=xlWhole)
'was anything found
If Not fnd Is Nothing Then
'record the first find
Set rng = fnd
addr = rng.Address
'loop and collect results until we arrive at the first find
Do
Set rng = Union(rng, fnd)
Set fnd = .FindNext(after:=fnd)
Loop Until addr = fnd.Address
'expand the found cells from the first row to the columns within the current region
With .Parent.Cells(1, 1).CurrentRegion
Set rng = Intersect(rng.EntireColumn, .Cells)
End With
'report the address(es) of the cell(s) found
Debug.Print rng.Address(0, 0)
Else
Debug.Print 'nothing found"
End If
End With
End With
End Sub
edited to correct some "optimization" typos
I think you want to select from a "headers" row all the cells whose value is on another range
If that's your goal you could try the following
Option Explicit
Function GetRange(fnameRng As Range, dataRng As Range) As Range
Dim fName As String
'get search criteria
fName = GetJoinFromRange(fnameRng)
With dataRng
.Rows(1).Insert
With .Offset(-1).Resize(1)
.FormulaR1C1 = "=if(isnumber(search(""-"" & R2C & ""-"" ,""" & fName & """)),1,"""")"
.Value = .Value
Set GetRange = .SpecialCells(xlCellTypeConstants)).Offset(1)
End With
.Rows(1).Offset(-1).EntireRow.Delete
End With
End Function
Function GetJoinFromRange(rng As Range) As String
If rng.Rows.Count > 1 Then
GetJoinFromRange = "-" & Join(Application.Transpose(rng), "-") & "-"
Else
GetJoinFromRange = "-" & Join(rng, "-") & "-"
End If
End Function
that can be called by a "main" sub like follows
Option Explicit
Sub main()
Dim fnameRng As Range, dataRng As Range, rngFound As Range
Set fnameRng = Workbooks("IntChk.xlsm").Worksheets("Data").Range("B3:B6") '<== adapt it to your needs
Set dataRng = ThisWorkbook.Worksheets("File").Range("B1:I1000") '<== adapt it to your needs
Set rngFound = GetRange(fnameRng, dataRng)
End Sub
after a week of trial and error, I was able to create this code. it works well and its light.
sub IntChk
Dim i As Integer
Lastcol = 5
For i = 1 To 1
For j = 1 To Lastcol
MsgBox "Cell Value = " & Cells(j) & vbNewLine & "Column Number = " & j
For Each c In Workbooks("IntChk.xlsm").Worksheets("Data").Range("A1:A50")
If c.Value = Cells(j) Then
MsgBox "Match"
Match = "True"
End If
Next c
Next j
If Match = "True" Then
MsgBox "Yes, True!"
Else:
MsgBox "not true ;("
End If
Next I
end sub

Summary Sheet That Updates Source Sheets

I'd like to make a summary sheet that, if changed, changes the source sheets it is pulling from. The code I have so far aggregates all of my sheets on the summary sheet on the summary sheet's activation event. I am trying to have all of my other sheets updated on the deactivation event but it does not seem to be working. Here is the code I am working with:
Private Sub Worksheet_Deactivate()
Application.ScreenUpdating = False
Dim tabs As Variant
tabs = Array("BELD", "RMLD", "Pascoag", "Devens", "WBMLP", "Rowely", "AMP", "First Energy", "Dynegy", "APN", "MISC")
For j = 1 To UBound(tabs)
Sheets(tabs(j)).Select
Dim rng1 As Range
Dim Stri As String
For i = 3 To ActiveSheet.UsedRange.Cells(ActiveSheet.UsedRange.Cells.Count).Row
Stri = ActiveSheet.Cells(i, "A")
Set rng1 = Worksheets("Summary").Range("A:A").Find(Stri, , xlValues, xlWhole)
If Not rng1 Is Nothing Then
Sheets("Summary").Range(rng1.Address).EntireRow.Copy
ActiveSheet.Range("A" & i).EntireRow.Select
Selection.Insert Shift:=xlLeft
ActiveSheet.Range("A" & i + 1).EntireRow.Select
Selection.Delete Shift:=xlUp
Else
MsgBox strSearch & " not found"
End If
Next
ActiveSheet.Range("A" & 1).Select
Next
Application.ScreenUpdating = True
End Sub
I am very new to vba and this is my first post on stackoverflow so if I missed anything just let me know.
When you assign a variant array in that manner, you will end up with a zero-based array. You need to start at j = 0. As your own code currently is, it will never access the BELD worksheet.
Dim tabs As Variant
tabs = Array("BELD", "RMLD", "Pascoag", "Devens", "WBMLP", "Rowely", "AMP", "First Energy", "Dynegy", "APN", "MISC")
For j = 0 To UBound(tabs)
....
A more universal method would be using For j = LBound(tabs) To UBound(tabs) which does not matter whether your array is 1 or 0 based as you let each array describe its own properties through the LBound function and UBound function.
A more comprehensive rewrite of your routine would include getting rid of the .Select and .Activate methods and use direct worksheet and cell referencing in its place.
Private Sub Worksheet_Deactivate()
Dim rng1 As Range
Dim Stri As String, lr As Long, j As Long, i As Long
Dim tabs As Variant
On Error GoTo bm_Safe_exit
Application.ScreenUpdating = False
Application.EnableEvents = False
tabs = Array("BELD", "RMLD", "Pascoag", "Devens", "WBMLP", "Rowely", _
"AMP", "First Energy", "Dynegy", "APN", "MISC")
For j = LBound(tabs) To UBound(tabs)
With Sheets(tabs(j))
lr = .Cells.Find(Chr(42), After:=.Cells(1, 1), SearchDirection:=xlPrevious).Row
For i = 3 To lr
Stri = .Cells(i, "A").Value
If CBool(Len(Stri)) Then
On Error Resume Next
With Me.Range("A:A")
Set rng1 = .Find(What:=Stri, After:=.Cells(.Rows.Count), LookIn:=xlValues, LookAt:=xlWhole)
End With
On Error GoTo bm_Safe_exit
If Not rng1 Is Nothing Then
'clearing then copy/paste may be better than inserting, pasting and ultimately deleting old row
.Rows(i).Clear
rng1.EntireRow.Copy _
Destination:=.Range("A" & i)
Else
'maybe copy the data from the sheet back to the summary sheet if this occurs
MsgBox Stri & " on " & .Name & " not found on Summary"
End If
End If
Next
End With
Next
bm_Safe_exit:
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Since this is in the Summary worksheet's code sheets, the use of Me can be applied to the Summary worksheet object. Once you have set rng1 to the range returned by the find, it is no longer necessary to describe the worksheet it comes from as its Range .Parent property is carried with it.
See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.

VBA Copy Paste string search

I can’t seem to figure out how to write a vba code that search’s through cells C10:G10 to find a match that equals cell A10, once found, copies range A14:A18 to the matched cell but below e.g F14:F18 (See Image)
Macro below
'Copy
Range("A14:A18").Select
Selection.Copy
'Paste
Range("F14:F18").Select
ActiveSheet.Paste!
Try this:
With Sheets("SheetName") ' Change to your actual sheet name
Dim r As Range: Set r = .Range("C10:G10").Find(.Range("A10").Value2, , , xlWhole)
If Not r Is Nothing Then r.Offset(4, 0).Resize(5).Value2 = .Range("A14:A18").Value2
End With
Range Object have Find Method to help you find values within your range.
The Range object that matches your search criteria is then returned.
To get your values to the correct location, simply use Offset and Resize Method.
Edit1: To answer OP's comment
To find formulas in Ranges, you need to set LookIn argument to xlFormulas.
Set r = .Range("C10:G10").Find(What:=.Range("A10").Formula, _
LookIn:=xlFormulas, _
LookAt:=xlWhole)
Above code find Ranges with exactly the same formula as Cell A10.
Dim RangeToSearch As Range
Dim ValueToSearch
Dim RangeToCopy As Range
Set RangeToSearch = ActiveSheet.Range("C10:G10")
Set RangeToCopy = ActiveSheet.Range("A14:A18")
ValueToSearch = ActiveSheet.Cells(10, "A").Value
For Each cell In RangeToSearch
If cell.Value = ValueToSearch Then
RangeToCopy.Select
Selection.Copy
Range(ActiveSheet.Cells(14, cell.Column), _
ActiveSheet.Cells(18, cell.Column)).Select
ActiveSheet.Paste
Application.CutCopyMode = False
Exit For
End If
Next cell
another additional variants
1.Using For each loop
Sub test()
Dim Cl As Range, x&
For Each Cl In [C10:G10]
If Cl.Value = [A10].Value Then
x = Cl.Column: Exit For
End If
Next Cl
If x = 0 Then
MsgBox "'" & [A10].Value & "' has not been found in range 'C10:G10'!"
Exit Sub
End If
Range(Cells(14, x), Cells(18, x)).Value = [A14:A18].Value
End Sub
2.Using Find method (already posted by L42, but a bit different)
Sub test2()
Dim Cl As Range, x&
On Error Resume Next
x = [C10:G10].Find([A10].Value2, , , xlWhole).Column
If Err.Number > 0 Then
MsgBox "'" & [A10].Value2 & "' has not been found in range 'C10:G10'!"
Exit Sub
End If
[A14:A18].Copy Range(Cells(14, x), Cells(18, x))
End Sub
3.Using WorksheetFunction.Match
Sub test2()
Dim Cl As Range, x&
On Error Resume Next
x = WorksheetFunction.Match([A10], [C10:G10], 0) + 2
If Err.Number > 0 Then
MsgBox "'" & [A10].Value2 & "' has not been found in range 'C10:G10'!"
Exit Sub
End If
[A14:A18].Copy Range(Cells(14, x), Cells(18, x))
End Sub
Here you go,
Sub DoIt()
Dim rng As Range, f As Range
Dim Fr As Range, Crng As Range
Set Fr = Range("A10")
Set Crng = Range("A14:A18")
Set rng = Range("C10:G19")
Set f = rng.Find(what:=Fr, lookat:=xlWhole)
If Not f Is Nothing Then
Crng.Copy Cells(14, f.Column)
Else: MsgBox "Not Found"
Exit Sub
End If
End Sub

Concatenate columns(user selected) and replace them with new column

I'm not an advanced VBA programmer. I'm working on an excel macro which will allow me to select a range(using input box) to clean the data(makes consistent with mySQL schema) on worksheet. I get this file from anther team and
1.) the order of columns is not fixed
2) levels of categories(there are few columns for categories like level1 level2 etc.) can be anything between 3-10.
I want to concatenate the columns for categories(in image level 1, level 2 etc.) using | as a separator and put the values in first category column(level1) while deleting remaining columns(level 2, level 3...[level 10]).
I removed some code from the end to reduce the length here but it still makes sense:
Sub cleanData()
Dim rngMyrange As Range
Dim cell As Range
On Error Resume Next
Do
'Cleans Status column
Set rngMyrange = Application.InputBox _
(Prompt:="Select Status column", Type:=8)
On Error GoTo 0
'Is a range selected? Exit sub if not selected
If rngMyrange Is Nothing Then
End
Else
Exit Do
End If
Loop
With rngMyrange 'with the range just selected
.Replace What:="Dead", Replacement:="Inactive", SearchOrder:=xlByColumns, MatchCase:=False
'I do more replace stuff here
End With
rngMyrange.Cells(1, 1) = "Status"
Do
'Concatenates Category Columns
Set rngMyrange = Application.InputBox _
(Prompt:="Select category columns", Type:=8)
On Error GoTo 0
'Is a range selected? Exit sub if not selected
If rngMyrange Is Nothing Then
End
Else
Exit Do
End If
Loop
With rngMyrange 'with the range just selected
'Need to concatenate the selected columns(row wise)
End With
rngMyrange.Cells(1, 1) = "Categories"
End Sub
Please do not suggest a UDF, I want to do this with macro. I must do this on files before importing them on SQL database, so a macro will be handy. Please ask if I failed to mention anything else.
EDIT: Image attached for illustration
UPDATE:
I now have a working code with help from vaskov17 on mrexcel but it does not delete the columns from where the levels are picked-level 2, level 3...etc. to shift next columns to left and the major challenge for me is to implement that code in my existing macro using range type instead of long type. I do not want to enter start column and finish column separately, instead I should be able to select range like in my original macro. Code for that macro is below, please help me:
Sub Main()
Dim start As Long
Dim finish As Long
Dim c As Long
Dim r As Long
Dim txt As String
start = InputBox("Enter start column:")
finish = InputBox("Enter ending column:")
For r = 2 To Cells(Rows.Count, "A").End(xlUp).Row
For c = start To finish
If Cells(r, c).Text <> "" Then
txt = txt & Cells(r, c).Text & "|"
Cells(r, c).Clear
End If
Next
If Right(txt, 1) = "|" Then
txt = Left(txt, Len(txt) - 1)
End If
Cells(r, start) = txt
txt = ""
Next
End Sub
I have removed the inputbox for selection of the category columns. Since they are always named Level x»y it makes it easier to find them automatically. That's why added a FindColumns() Sub to your code. It assigns the first fCol and last lCol Category column to global variables.
The ConcatenateColumns() concatenates cells in each row using "|" as separator.
The DeleteColumns() deletes the other columns
Cells(1, fCol).Value = "Category renames Level 1 to Category and Columns.AutoFit resizes all columns widths to fit the text.
Code:
Option Explicit
Dim fCol As Long, lCol As Long
Sub cleanData()
Dim rngMyrange As Range
Dim cell As Range
On Error Resume Next
Do
'Cleans Status column
Set rngMyrange = Application.InputBox _
(Prompt:="Select Status column", Type:=8)
On Error GoTo 0
'Is a range selected? Exit sub if not selected
If rngMyrange Is Nothing Then
End
Else
Exit Do
End If
Loop
With rngMyrange 'with the range just selected
.Replace What:="Dead", Replacement:="Inactive", SearchOrder:=xlByColumns, MatchCase:=False
'I do more replace stuff here
End With
rngMyrange.Cells(1, 1) = "Status"
' Concatenate Category Columns
FindColumns
ConcatenateColumns
DeleteColumns
Cells(1, fCol).Value = "Category"
Columns.AutoFit
End Sub
Private Sub FindColumns()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim i As Long, j As Long
For i = 1 To ws.Cells(1, Columns.Count).End(xlToLeft).Column
If StrComp(ws.Cells(1, i).Text, "Level 1", vbTextCompare) = 0 Then
For j = i To ws.Cells(1, Columns.Count).End(xlToLeft).Column
If InStr(1, ws.Cells(1, j).Text, "Level", vbTextCompare) Then
lCol = j
End If
Next j
fCol = i
Exit Sub
End If
Next i
End Sub
Private Sub ConcatenateColumns()
Dim rng As Range
Dim i As Long, j As Long
For i = 2 To Cells(Rows.Count, fCol).End(xlUp).Row
Set rng = Cells(i, fCol)
For j = fCol + 1 To lCol
rng = rng & "|" & Cells(i, j)
Next j
rng = "|" & rng & "|"
Set rng = Nothing
Next i
End Sub
Private Sub DeleteColumns()
Dim i As Long
For i = lCol To fCol + 1 Step -1
Columns(i).Delete Shift:=xlToLeft
Next i
End Sub