compare and replace column value in excel vba macro [closed] - excel

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I want to compare "Master" worksheet with "New" worksheet based on first column value. If same is available in "New" worksheet, then I want to compare column "E" of matched row of "Master" worksheet with column "E" of matched row of "New" worksheet. If there is any diffenrence in value then replace column value "E" of "master" by column value "E" of "New" and highlight entire row by color.
Sub CompareValues()
Dim sh1 As Worksheet, sh2 As Worksheet, sh3 As Worksheet, lr As Long, rng As Range, c As Range
Set sh1 = Sheets("New")
Set sh2 = Sheets("Master")
lr = sh2.Cells(Rows.Count, 1).End(xlUp).Row 'Get last row with data in Column A on Sheet 1.
Set rng = sh2.Range("A2:A" & lr) 'Set compare range to a variable
For Each c In rng 'Make cell by cell comparison
If Application.CountIf(sh1.Range("A:A"), c.Value) <> 0 Then
If c.EntireRow.Range("E") <> sh1.Range("E", c.Value) Then
' to fill the value into another sheet simply replace from sh1 to c.Range
c.Range("E" & i).Copy (sh1.Range("E" & i))
Range(c, sh2.Cells(c.Row, Columns.Count).End(xlToLeft)).Interior.ColorIndex = 4
End If
End If
Next
End Sub

Unfortunately I cannot comment on this question (or post pictures)...
To clarify (assuming my third column would be column 'E'):
If sheet "Master" looks like this:
| first | info | d'oh |
| two | info | 4 |
| three | info | hello |
and "New" looks like that:
| first | info | d'oh |
| two | blub | 5 |
| wheee | cool | cool |
you want that as a result:
| first | info | d'oh |
| two | info | 5 | <- highlighted
| three | info | hello |
My solution:
Sub UpdateSheet()
Dim masterSheet As Excel.Worksheet, newSheet As Excel.Worksheet
Dim e, masterCell As Excel.Range, newCell As Excel.Range
Dim columnOffset As Integer
Const idColumn = 1 'column A has index 1
Const newDataColumn = 5 'column E has index 5
columnOffset = newDataColumn - idColumn 'offset between those columns is 4
Set masterSheet = ThisWorkbook.Sheets("Master")
Set newSheet = ThisWorkbook.Sheets("New")
'iterate over all cells of the first column in the used range of this worksheet
For Each e In masterSheet.UsedRange.Columns(idColumn).Cells
Set masterCell = e
Set newCell = newSheet.Cells(masterCell.Row, idColumn)
'if the cell on the master sheet is not empty and the values of both cells match
If masterCell.Value <> Empty And masterCell.Value = newCell.Value Then
'select cells in column "E"
Set masterCell = masterCell.Offset(0, columnOffset)
Set newCell = newCell.Offset(0, columnOffset)
'copy values and paint row if values don't match
If masterCell.Value <> newCell.Value Then
masterCell.Value = newCell.Value
masterCell.EntireRow.Interior.ColorIndex = 4
End If
End If
Next e
End Sub

I updated your solution to fit my requirement. Thanks for your help.
Sub UpdateSheet()
Dim masterSheet As Excel.Worksheet, newSheet As Excel.Worksheet
Dim e, n, masterCell As Excel.Range, newCell As Excel.Range
Dim columnOffset As Integer
Const idColumn = 1 'column A has index 1
Const newDataColumn = 5 'column E has index 5
columnOffset = newDataColumn - idColumn 'offset between those columns is 4
Set masterSheet = ThisWorkbook.Sheets("Master")
Set newSheet = ThisWorkbook.Sheets("New")
'iterate over all cells of the first column in the used range of this worksheet
For Each e In masterSheet.UsedRange.Columns(idColumn).Cells
Set masterCell = e
If masterCell.Value <> Empty Then
For Each n In newSheet.UsedRange.Columns(idColumn).Cells
Set newCell = n
'if the cell on the master sheet is not empty and the values of both cells match
If masterCell.Value = newCell.Value Then
'select cells in column "E"
Set masterCell = masterCell.Offset(0, columnOffset)
Set newCell = newCell.Offset(0, columnOffset)
'copy values and paint row if values don't match
If masterCell.Value <> newCell.Value Then
masterCell.Value = newCell.Value
masterCell.EntireRow.Interior.ColorIndex = 4
End If
End If
Next n
End If
Next e
End Sub

Related

How Do I Add 3rd Range to Reconcile?

I'm looking to create a tool which will reconcile values across 3 spreadsheets. For example, Sheet1: ID = 123A, Country = IRELAND; Sheet2: ID = 123A, COUNTRY = UK; Sheet3: ID = 123A, COUNTRY = UK. If it does not match, we will get a FALSE value.
Currently, I have the capacity to reconcile 2 files in columns B & C in shtRecon and am looking to add a 3rd value in column D which will reconcile 3 files instead of 2.
In other words, when column B = column C, there are 0 errors, when column B <> column C, there is 1 error. I'd like it to be column B = column C = column D instead. I've included my working code for 2 column reconciliation beneath. Any ideas?
Sub ReconcileVenueRef()
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim shtRecon As Worksheet
Dim shtSummary As Worksheet
Dim rng1 As Range
Dim rng2 As Range
Dim cel As Range
Dim oDict As Object
Dim reconRow As Long
Dim noOfErrors As Long
Dim ETL As Variant
Dim MiFID As Variant
Dim Match As Boolean
Set sht1 = Worksheets("MiFID Export")
Set sht2 = Worksheets("ETL")
Set shtRecon = Worksheets("Reconciliation")
Set shtSummary = Worksheets("Summary")
'Define the columns that you need, and find the last row
Set rng1 = sht1.Range("A2:B" & sht1.Range("A" & sht1.Rows.Count).End(xlUp).Row)
Set rng2 = sht2.Range("A2:B" & sht2.Range("A" & sht2.Rows.Count).End(xlUp).Row)
'Get a dictionary object holding unique values from sheet 2
'In my case, column A holds the Id, and column B holds the value
Set oDict = GetDictionary(rng2, 1, 4)
reconRow = 0
'Loop over each cel in sheet 1, column 1
'Look for the value in column 1 in the sheet 2 dictionary
'object, and then reconcile
For Each cel In Intersect(rng1, sht1.Columns(1))
'Get the next avail row in reconciliation sheet
reconRow = reconRow + 1
shtRecon.Range("A" & reconRow).Value = cel.Value
'Recon column B holds value from sheet 1, column B
shtRecon.Range("B" & reconRow).Value = cel.Offset(, 3).Value
'If Id is found in Sheet B dictionary, then take the value
'otherwise, write "blank" in column C
'ETL spreadsheet
If oDict.exists(cel.Value) Then
shtRecon.Range("C" & reconRow).Value = oDict(cel.Value)
Else
shtRecon.Range("C" & reconRow).Value = "BLANK"
End If
Next cel
'If ETL cell = MiFID cell then noOfErrors is 0, otherwise it's 1
noOfErrors = 0
ETL = shtRecon.Cells(1, 3).Value
MiFID = shtRecon.Cells(1, 2).Value
If ETL = MiFID Then
Match = True
shtRecon.Cells(1, 4).Value = Match
shtSummary.Cells(4, 3).Value = noOfErrors
ElseIf ETL <> MiFID Then
Match = False
shtRecon.Cells(1, 4).Value = Match
noOfErrors = noOfErrors + 1
shtSummary.Cells(4, 3).Value = noOfErrors
End If
End Sub
'Function stores a range into a dictionary
'Param inputRange takes a range to be stored
'Param idColumn takes the column of the range to be used as the ID
'e.g. if idColumn = 2, and inputRange("C1:F10"), then column D is used for ID
'Param valueColumn points to the column in range used for the value
Function GetDictionary(inputRange As Range, idColumn As Long, valueColumn As Long) As Object
Dim oDict As Object
Dim sht As Worksheet
Dim cel As Range
Set oDict = CreateObject("Scripting.Dictionary")
Set sht = inputRange.Parent
For Each cel In Intersect(inputRange, inputRange.Columns(idColumn))
If Not oDict.exists(cel.Value) Then
oDict.Add cel.Value, sht.Cells(cel.Row, valueColumn).Value
End If
Next cel
Set GetDictionary = oDict
End Function

Write a random value in a cell if the cell has the same value that can be found in another sheet

I have the below two sheets in an excel file. I need a VBA code that will write in the column Status the value "Completed" but only if the ID is found in Sheet2. So for example, in Sheet1 I want the ID 1 to be with status "Completed", but ID 2 with blank cell in Status, because ID2 cannot be found in Sheet2. I would like to do this with a for each, as it will work faster than a simple IF formula, but I can't seem to find a code that would work. Thank you
Sheet1:
----------------------------------
ID | Product | Date | Status
-----------------------------------
1 | abc | 05-Jan-19 |
2 | abc | 07-Jan-18 |
3 | def | 05-Apr-19 |
4 | ghi | 06-Feb-19 |
Sheet2:
-------------
ID | Product
-------------
1 | abc
3 | def
4 | ghi
Use array is fast.
Sub setStatus()
Dim Ws1 As Worksheet, Ws2 As Worksheet
Dim rngDB As Range
Dim vDB, vR()
Dim i As Long, n As Long
Set Ws1 = Sheets(1)
Set Ws2 = Sheets(2)
With Ws1
vDB = .Range("a2", .Range("a" & Rows.Count).End(xlUp))
End With
With Ws2
Set rngDB = .Range("a2", .Range("a" & Rows.Count).End(xlUp))
End With
n = UBound(vDB, 1)
ReDim vR(1 To n, 1 To 1)
For i = 1 To n
If WorksheetFunction.CountIf(rngDB, vDB(i, 1)) Then
vR(i, 1) = "Completed"
End If
Next i
Ws1.Range("d2").Resize(n) = vR
End Sub
=IF(ISNA(MATCH(A4;Sheet2!$A$2:$A$6;0));"";"Completed")
A4 is a cell in Status column from Sheet1
$A$2:$A$6 is the range of ids from Sheet2.
Just apply this formula to all cells in Status column from Sheet1.
TheReddsable you can try below code also
Option Explicit
Dim awb, product_id As String
Dim sht_1_count, sht_2_count, loop_i, loop_d As Double
Sub get_status()
awb = ActiveWorkbook.Name
sht_1_count = Workbooks(awb).Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
sht_2_count = Workbooks(awb).Sheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Row
For loop_i = 2 To sht_1_count
product_id = Workbooks(awb).Sheets("Sheet1").Cells(loop_i, 1)
For loop_d = 2 To sht_2_count
If LCase(Trim(product_id)) = LCase(Trim(Workbooks(awb).Sheets("Sheet2").Cells(loop_d, 1))) Then
Workbooks(awb).Sheets("Sheet1").Cells(loop_i, 4) = "Completed"
Exit for
End If
Next loop_d
Next loop_i
End Sub
I made the code with the assumption that both ranges start from A1. Please, test it!
Sub BringVal()
Dim sh1 As Worksheet, sh2 As Worksheet, arrCheck As Variant, arrMatch As Variant
Dim lastRow1 As Long, lastRow2 As Long, i As Long, j As Long, arrRez As Variant
Dim boolF As Boolean
Set sh1 = Sheets(1): Set sh2 = Sheets(2) 'use here your real sheets!
lastRow1 = sh1.Range("A" & sh1.Rows.count).End(xlUp).Row
lastRow2 = sh2.Range("A" & sh2.Rows.count).End(xlUp).Row
arrCheck = sh1.Range("A2:A" & lastRow1).Value
arrMatch = sh2.Range("A2:B" & lastRow2).Value
ReDim arrRez(1 To UBound(arrCheck))
For i = 1 To UBound(arrCheck)
For j = 1 To UBound(arrMatch, 1)
If arrCheck(i, 1) = arrMatch(j, 1) Then
boolF = True
arrRez(i) = arrMatch(j, 2): Exit For
End If
Next j
If Not boolF Then arrRez(i) = Empty
Next i
If UBound(arrRez) > 0 _
Then sh1.Range("D2:D" & UBound(arrRez) + 1).Value = _
WorksheetFunction.Transpose(arrRez)
End Sub
The code should be extremely fast, since it works only in memory and drop all the collected data at once.
If you need a message for the case of not any match found, it is so easy to add an Else ... End If sequence after the last If...

How do you pass a cell or range into InStr?

I'm trying to copy rows from one worksheet to another based on whether a string exists in a specific cell of each row. In the below example, I'm searching for Jordan in Column J. If that name is in this particular rows Column J, it gets moved to a different sheet (Final Sheet).
Sub Test()
Worksheets("All Data").Activate
Dim N As Long, i As Long
N = Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To N
If InStr(1, Cells(i, "J"), "Jordan") > 0 Then
Worksheets("All Data").Rows(i).Copy
Worksheets("Final Sheet").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
End If
Next i
End Sub
What I want to do is look for multiple strings. I can accomplish this by adding as many "Or" are needed like below.
If InStr(1, Cells(i, "J"), "Jordan") > 0 Or InStr(1, Cells(i, "J"), "Barkley") > 0 Then
I usually have 5+ strings i'm searching for and it becomes difficult to update the code each time. I would rather the strings I look for be located in a range of cells on some hidden sheet that I or someone can update easily. I've been tinkering with the below. Range does work if its a single cell. If its more such as A1:A5 then it breaks. Any thoughts on how I could accomplish this? Am I totally missing an elegant solution?
Sub Test()
Worksheets("All Data").Activate
Dim N As Long, i As Long
N = Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To N
If InStr(1, Cells(i, "J"), Worksheets("List").Range("A1:A5")) > 0 Then
Worksheets("All Data").Rows(i).Copy
Worksheets("Final Sheet").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
End If
Next i
End Sub
List Sheet
- | A |
1 | Jordan |
2 | Barkley |
3 | Batman |
4 | Robin |
5 | Ozzy |
Based on this previous answer, I customize it to your scenario
Remember to backup your data before running it.
Read the code's comments and adjust the variables' values to fit your needs.
Public Sub CopyData()
' Define the object variables
Dim sourceWorksheet As Worksheet
Dim targetWorksheet As Worksheet
Dim listRange As Range
Dim evalCell As Range
' Define other variables
Dim listRangeAddress As String
Dim startSourceRow As Long
Dim lastSourceRow As Long
Dim columnForLastRowSource As Long
Dim lastTargetRow As Long
Dim sourceRowCounter As Long
Dim columnForLastRowTarget As Long
Dim columnToEval As Long
''''' Adjust the folloing values ''''
' Set the lookup list range address
listRangeAddress = "B1:B5"
' Adjust the worksheets names
Set sourceWorksheet = ThisWorkbook.Worksheets("All Data")
Set targetWorksheet = ThisWorkbook.Worksheets("Final Sheet")
Set listRange = ThisWorkbook.Worksheets("List").Range(listRangeAddress)
' Set the initial row where data is going to be evaluated
startSourceRow = 1
' Set the column from which you're going to get the last row in sourceSheet
columnForLastRowSource = 1
' Set the column from which you're going to get the last row in targetSheet
columnForLastRowTarget = 1
' Set the column where you evaluate if condition is met
columnToEval = 10
'''''''Loop to copy rows that match'''''''
' Find the number of the last row in source sheet
lastSourceRow = sourceWorksheet.Cells(sourceWorksheet.Rows.Count, columnForLastRowSource).End(xlUp).Row
For sourceRowCounter = startSourceRow To lastSourceRow
For Each evalCell In listRange.Cells
' Evaluate if criteria is met in column
If InStr(sourceWorksheet.Cells(sourceRowCounter, columnToEval).Value, evalCell.Value) > 0 Then
' Get last row on target sheet (notice that this search in column A = 1)
lastTargetRow = targetWorksheet.Cells(targetWorksheet.Rows.Count, columnForLastRowTarget).End(xlUp).Row
' Copy row to target
sourceWorksheet.Rows(sourceRowCounter).Copy targetWorksheet.Rows(lastTargetRow + 1)
' If found, don't keep looking
Exit For
End If
Next evalCell
Next sourceRowCounter
End Sub
Let me know if it works and remember to mark the answer if it does.

Searching for a value/string and aditional endings in another worksheet

I want to use each value/string in a certain column (A1, A2, A3...) in worksheet 1 to search a certain range in worksheet 2 for that value/string alone and (!) with additional endings.
Example: Use in worksheet 1 A1 = K-1234 and search in a defined range in worksheet 2 for the string K-1234 and combinations of K-1234 with /x, /y, /z. Whenever you find such a combination copy the whole row from worksheet 2 into a new worksheet 3.
Using column A in worksheet 1:
worksheet 1
A
A1 = K-1234
A2 = Y-1234
A3 = RP-78
…
A1000 = Z/34-1
Searching in worksheet 2 in the range B1:E3 for A1, A1/x, A1/y and A1/z:
worksheet 2
A B C D E
GHJ A1/x 456 G5G F1-1
FF- A1 23-A TTR BV1
8/a A1/z bnR 34-1 bn/1
That's how worksheet 3 should look like after using A1 from worksheet 1 to search in worksheet 2:
worksheet 3
A B C D E
FF- A1 23-A TTR BV1
GHJ A1/x 456 G5G F1-1
8/a A1/z bnR 34-1 bn/1
or with A1 written out:
worksheet 3
A B C D E
FF- K-1234 23-A TTR BV1
GHJ K-1234/x 456 G5G F1-1
8/a K-1234/z bnR 34-1 bn/1
(A1/y doesn't exist)
Continue with A2, A2/x, A2/y and A2/z and so on till the end of the column (for example A1000).
Hope I could explain the problem sufficiently. I would be very thankful for any suggestion.
You could try:
Option Explicit
Sub CopyYes()
Dim i As Long, LastRow1 As Long, LastRow3 As Long
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Dim rngToSearch As Range, rngFound As Range
With ThisWorkbook
Set ws1 = .Worksheets("Sheet1")
Set ws2 = .Worksheets("Sheet2")
Set ws3 = .Worksheets("Sheet3")
End With
Set rngToSearch = ws2.Range("B1:E3")
With ws1
LastRow1 = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 1 To LastRow1
Set rngFound = rngToSearch.Find(.Range("A" & i).Value & "*", LookIn:=xlValues)
If Not rngFound Is Nothing Then
LastRow3 = ws3.Cells(ws3.Rows.Count, "A").End(xlUp).Row
If LastRow3 = 1 And ws3.Range("A1").Value = "" Then
LastRow3 = 1
Else
LastRow3 = LastRow3 + 1
End If
ws2.Range("B" & rngFound.Row & ":E" & rngFound.Row).Copy
ws3.Range("A" & LastRow3).PasteSpecial Paste:=xlPasteValues
End If
Next i
End With
End Sub

Creating an Excel Macro to populate values

Creating macros in Excel is not my strong point so I'm wondering if someone is able to help.
I have a small table with product values, though not every cell has a value. What I'm trying to do is write a macro to create a list on a separate sheet. The macro I have written works for the first column but that's where it stops.
For example
List | aa | bb | cc
a |1 | 15 | -
b |2 | 23 | 12
c |- | 17 | 5
d |4 | - | -
Should appear on Sheet 2 like so
- List| aa
- a | 1
- b | 2
- d | 4
- List| bb
- a | 15
- b | 23
- c | 17
- List| cc
- b | 12
- c | 5
At the moment, only aa shows correctly on the 2nd sheet and none of the other columns.
The macro I have so far is
Sub Button2_Click()
Dim Column As Integer
Column = 1
newrow = 1
Do Until Worksheets("Sheet1").Cells(Column, 1).Value = ""
If Worksheets("Sheet1").Cells(Column, 2).Value <> "" Then
Worksheets("Sheet2").Cells(newrow, 1).Value = Worksheets("Sheet1").Cells(Column, 1).Value
Worksheets("Sheet2").Cells(newrow, 2).Value = Worksheets("Sheet1").Cells(Column, 2).Value
newrow = newrow + 1
End If
Column = Column + 1
Loop
End Sub
This is what I was suggesting. This code sample is based on the above sample data. If the structure of the sample changes then you will have to amend the code accordingly. I have commented the code so that you shouldn't have a problem understanding it. But if you do, simply post back :)
CODE
Option Explicit
Sub Sample()
'~~> Input/Output Sheets
Dim wsI As Worksheet, wsO As Worksheet
Dim Lrow As Long, ORow As Long, i As Long
Dim rngToFilter As Range
'~~> Set the input, output sheets
Set wsI = ThisWorkbook.Sheets("Sheet1")
Set wsO = ThisWorkbook.Sheets("Sheet2")
'~~> Set the output row in the new sheet
ORow = 1
With wsI
'~~> Get last row in Col A
Lrow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Set your range
Set rngToFilter = .Range("A1:D" & Lrow)
'~~> Hide Col C to E
.Range("C:E").EntireColumn.Hidden = True
'~~> Loop through Col B to Col D
For i = 2 To 4
'~~> Remove any filters
.AutoFilterMode = False
'~~> Copy Header viz List| aa, List| bb
Union(.Cells(1, 1), .Cells(1, i)).Copy wsO.Range("A" & ORow)
'~~> Get next empty row
ORow = ORow + 1
'~~> Filter, offset(to exclude headers) and copy visible rows
With rngToFilter
.AutoFilter Field:=i, Criteria1:="<>"
'~~> Copy the filtered results to the new sheet
.Offset(1, 0).SpecialCells(xlCellTypeVisible).Copy wsO.Range("A" & ORow)
End With
ORow = wsO.Range("A" & wsO.Rows.Count).End(xlUp).Row + 1
'~~> Unhide/Hide relevant columns
.Columns(i).EntireColumn.Hidden = True
.Columns(i + 1).EntireColumn.Hidden = False
'~~> Remove any filters
.AutoFilterMode = False
Next i
'~~> Unhide all columns
.Range("B:E").EntireColumn.Hidden = False
End With
End Sub
SCREENSHOT

Resources