VBA Loop to Highlight Inconsistencies - vba

I have a spreadsheet with two columns (A and B). I would like to (FOR) loop through column B until two or more of the cell values match. For the cells that match in column B, I would like to loop through their corresponding values in column A. If their corresponding values are not identical, I want all of the rows involved to be highlighted.
I know it's not right/complete, but below is the basic structure I would like to follow. Any and all help is greatly appreciated. Thank you.
Sub MySUb()
Dim iRow As Integer
For iRow = 2 To ActiveSheet.UsedRange.Rows.Count
If Trim(range("A" & iRow)) <> "" And Trim(range("B" & iRow)) = Trim(range("B" & iRow)) Then
range("A" & iRow, "B" & iRow).Interior.ColorIndex = 6
End If
Next
End Sub

You can first sort based on Column B, then modify your code to:
Sub MySUb()
Dim iRow As Integer
For iRow = 1 To ActiveSheet.UsedRange.Rows.Count
If Trim(Range("A" & iRow).Text) <> "" And _
Trim(Range("B" & iRow).Text) = Trim(Range("B" & iRow + 1).Text) And _
Trim(Range("A" & iRow).Text) <> Trim(Range("A" & iRow + 1).Text) Then
Range("A" & iRow, "B" & iRow).Interior.ColorIndex = 6
Range("A" & iRow + 1, "B" & iRow + 1).Interior.ColorIndex = 6
End If
Next
End Sub
EDIT:
Here is a better solution which can handle the case where in column B there >2 matching cells, but the corresponding cells in A do not match (i.e. at least one of them is different). In this case all of those cells are marked.
Sub MySUb()
Dim iRow As Integer
Dim jRow As Integer
Dim kRow As Integer
For iRow = 1 To ActiveSheet.UsedRange.Rows.Count
'If Trim(Range("A" & iRow).Text) <> "" Then
For jRow = iRow To ActiveSheet.UsedRange.Rows.Count 'Finds the last non-matching item in B
If Trim(Range("B" & jRow).Text) <> Trim(Range("B" & iRow).Text) Then
Exit For
End If
Next jRow
For kRow = iRow To jRow - 1
If Trim(Range("A" & iRow).Text) <> Trim(Range("A" & kRow).Text) Then
Range("A" & iRow, "B" & kRow).Interior.ColorIndex = jRow + 1 'Or can be 6
End If
Next kRow
Next iRow
End Sub

How about something like this, using a dictionary to track the instances of an item in Column B and then testing the Column A values for each unique instance of Column B values. If one fails to match then all instances are marked.
Sub DuplicateChecker()
Dim rngColumnB As Range
Set rngColumnB = Range("B2", Range("B2").End(xlDown))
Dim rngCell As Range
Dim rngDupe As Range
Dim rngDuplicateB As Range
Dim dctValuesChecked As Dictionary
'requires enabled reference library for 'Microsoft Scripting Runtime'
Set dctValuesChecked = New Dictionary
Dim strColumnAValue As String
For Each rngCell In rngColumnB
strColumnAValue = rngCell.Offset(0, -1).Value
If Not dctValuesChecked.Exists(Trim(rngCell.Value)) Then
Call dctValuesChecked.Add(rngCell.Value, rngCell.Row)
Else
Set rngDuplicateB = FindItemsInRange(rngCell.Value, rngColumnB)
rngDuplicateB.EntireRow.Select
For Each rngDupe In rngDuplicateB
If Not rngDupe.Offset(0, -1).Value = strColumnAValue Then
rngDuplicateB.Interior.ColorIndex = 6
rngDuplicateB.Offset(0, -1).Interior.ColorIndex = 6
End If
Next rngDupe
End If
Next rngCell
End Sub
Function FindItemsInRange(varItemToFind As Variant, _
rngSearchIn As Range, _
Optional LookIn As XlFindLookIn = xlValues, _
Optional LookAt As XlLookAt = xlPart, _
Optional blnMatchCase As Boolean = False) As Range
'adapted from a function by Aaron Blood found on the Ozgrid forums:
'http://www.ozgrid.com/forum/showthread.php?t=27240
With rngSearchIn
Dim rngFoundItems As Range
Set rngFoundItems = .Find(What:=varItemToFind, _
LookIn:=LookIn, _
LookAt:=LookAt, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=blnMatchCase, _
SearchFormat:=False)
If Not rngFoundItems Is Nothing Then
Set FindItemsInRange = rngFoundItems
Dim strAddressOfFirstFoundItem As String
strAddressOfFirstFoundItem = rngFoundItems.Address
Do
Set FindItemsInRange = Union(FindItemsInRange, rngFoundItems)
Set rngFoundItems = .FindNext(rngFoundItems)
Loop While Not rngFoundItems Is Nothing And _
rngFoundItems.Address <> strAddressOfFirstFoundItem
End If
End With
End Function

Related

finding the first and last entry with a certain value in a row

I have a excel sheet containing multiple cells with a string foo in the first row. I want to find the first and last column in which the string is written. I have tried the following
Dim first_col As Integer
Dim last_col As Integer
Dim col As Integer
Dim found As Range
Dim ws_MLB as Worksheet
Dim foo as String
set ws_MLB = ThisWorkbook.Sheet(1)
Set found = ws_MLB.Rows(1).Find(foo)
If Not found Is Nothing Then
col = found.Column
first_col = col
last_col = col
Do
found = ws_MLB.Rows(1).FindNext(found)
col = found.Column
If col < first_col Then
first_col = col
MsgBox ("This should not happen")
ElseIf col > last_col Then
last_col = col
End If
Loop While Not found Is Nothing And col <> first_col
Else
MsgBox ("not found")
End If
But this way I only get the the first value for both first_col and last_col. When I search for the string with the integrated excel search I find multiple instances. So the string is there. Have I done a mistake or is there a better way to do this?
edit forgot to mention that I also tried to change the search direction, but I still got the first entry.
You can make this a lot easier by using the SearchDirection Parameter in .Find by using xlNext you search Left to Right then xlPrevious searches Right to Left.
Sub FindFL()
Dim wbk As Workbook
Dim ws As Worksheet
Dim fColumn As Long, lColumn As Long
Set wbk = ThisWorkbook 'Change this to your workbook
Set ws = wbk.Worksheets("Sheet1") 'Change this to your worksheet
With ws
'Find first column that foo shows up
fColumn = .Cells.Find(What:="foo", _
After:=.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=False).Column
'Find last column that foo shows up
lColumn = .Cells.Find(What:="foo", _
After:=.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
Debug.Print "First Column is " & fColumn; vbNewLine _
; "Last Column is " & lColumn
End With
End Sub
I would do that like this:
Public Sub foo()
Dim nCol As Integer
Dim nFirst As Integer
Dim nLast As Integer
With ActiveSheet
nCol = 1
Do Until .Cells(1, nCol) = ""
If .Cells(1, nCol) = "foo" Then
If nFirst = 0 Then
nFirst = nCol
Else
nLast = nCol
End If
End If
nCol = nCol + 1
Loop
End With
MsgBox "First: " & nFirst & vbCr & "Last: " & nLast
End Sub

How to extract excel cell values delimited with filters?

In the each cell in a column I have this information in the cells:
A1 values:
Depth=standard;Size=1 section;Doors=hinged solid;Interior configuration=shelves;Compressor HP=1/2 HP;Interior finish=stainless steel;Exterior finish=stainless steel;Refrigeration=top mount self-contained
A2 values:
Top openings= 6 pan;Size=1 section;Compressor HP=1/6 HP;Style=drawers;Exterior finish=stainless steel;Interior finish=stainless steel;Refrigeration=rear mounted
A3,A4,A5 etc all follow similar formats
I need some method of abstracting out the following information into its own cells:
I need each semicolon separated value to be checked if there is a column name for it already, if not, make a new column and put all corresponding values where they need to be
I thought about using text->columns and then using index/match but I haven't been able to get my match criteria to work correctly. Was going to do this for each unique column. Or do I need to use VBA?
You could go with something like this, though you'll have to update the sheet name and probably where you want the final data located.
Sub SplitCell()
Dim DataFromCell, FoundCell
Dim Testing, Counted, LastCol
For Each c In Range(Range("A1"), Range("A" & Rows.count).End(xlUp))
Testing = Split(c.Value, ";")
Range("B" & c.row + 1).Value = "A" & c.row
Counted = UBound(Testing)
For Each x In Testing
DataFromCell = Split(x, "=")
With Sheet2
Set FoundCell = .Cells.Find(What:=DataFromCell(0), after:=.Cells(1, 2), _
LookIn:=xlValues, lookat:=1, searchorder:=xlByColumns, searchdirection:=xlNext, _
MatchCase:=False, searchformat:=False)
End With
If Not FoundCell Is Nothing Then
Cells(c.row + 1, FoundCell.Column).Value = DataFromCell(1)
End If
If FoundCell Is Nothing Then
LastCol = Sheet2.Cells(1, Sheet2.Columns.count).End(xlToLeft).Column
Cells(1, LastCol + 1).Value = DataFromCell(0)
Cells(c.row + 1, LastCol + 1).Value = DataFromCell(1)
End If
Next x
Next c
End Sub
Edit
Since the above was giving you errors you could try this one:
Sub SplitCell()
Dim DataFromCell, FoundCell
Dim Testing, Counted, LastCol
For Each c In Range(Range("A1"), Range("A" & Rows.count).End(xlUp))
Testing = Split(c.Value, ";")
Range("B" & c.row + 1).Value = "A" & c.row
Counted = UBound(Testing)
For Each x In Testing
DataFromCell = Split(x, "=")
LastCol = Sheet2.Cells(1, Sheet2.Columns.count).End(xlToLeft).Column
With Sheet2
FoundCell = Application.Match(DataFromCell(0), Range(Cells(1, 2), Cells(1, LastCol)), 0)
'Set FoundCell = .Cells.Find(What:=DataFromCell(0), after:=.Cells(1, 2), _
LookIn:=xlValues, lookat:=1, searchorder:=xlByColumns, searchdirection:=xlNext, _
MatchCase:=False, searchformat:=False)
End With
If Not IsError(FoundCell) Then
Cells(c.row + 1, FoundCell + 1).Value = DataFromCell(1)
End If
If IsError(FoundCell) Then
Cells(1, LastCol + 1).Value = DataFromCell(0)
Cells(c.row + 1, LastCol + 1).Value = DataFromCell(1)
End If
Next x
Next c
End Sub
Only changed a few things so that it is using Match instead of Find
My solution below works as intended but the data wasn't as formatted as I originally thought.
Option Explicit
Private Sub Auto_Open()
MsgBox ("Welcome to the delimiter file set.")
End Sub
'What this program does:
'http://i.imgur.com/7MVuZLt.png
Sub DelimitFilter()
Dim curSpec As String
Dim curSpecArray() As String
Dim i As Integer, IntColCounter As Integer, iCounter As Integer, argCounter As Integer
Dim WrdString0 As String, WrdString1 As String
Dim dblColNo As Double, dblRowNo As Double
Worksheets(1).Activate
'Reference to cell values that always have data associated to them
Range("W2").Activate
'checks for number of arguments to iterate through later
Do
If ActiveCell.Value = "" Then Exit Do
ActiveCell.Offset(1, 0).Activate
argCounter = argCounter + 1
Loop
'Check # of arguments
Debug.Print (argCounter)
'Values to delimit
Range("X2").Activate
IntColCounter = 1
'Loop each row argument
For iCounter = 0 To argCounter
'Set var to activecell name
dblColNo = ActiveCell.Column
dblRowNo = ActiveCell.Row
'Grab input at active cell
curSpecArray() = Split(ActiveCell.Value, ";")
'Ignore empty rows
If Not IsEmpty(curSpecArray) Then
'Iterate every delimited active cell value at that row
For i = LBound(curSpecArray) To UBound(curSpecArray)
'Checks for unique attribute name, if none exists, make one
WrdString0 = Split(curSpecArray(i), "=")(0)
'a large range X1:ZZ1 is used as there are many unique column names
If IsError(Application.Match(WrdString0, Range("X1:ZZ1"), 0)) Then 'if NOT checks if value exists
Cells(1, dblColNo + IntColCounter).Value = WrdString0
IntColCounter = IntColCounter + 1
End If
'Output attribute value to matching row and column
WrdString1 = Trim(Split(curSpecArray(i), "=")(1))
Debug.Print (WrdString1)
Cells(dblRowNo, -1 + dblColNo + Application.Match(WrdString0, Range("X1:ZZ1"), 0)).Value = WrdString1
Next i
End If
'Iterate Next row value
ActiveCell.Offset(1, 0).Activate
Next iCounter
End Sub

Loops through the rows of a single column in Set Range

I am trying to iterate through the rows of a single column in the set range. I set the range as WorkingRange and then set the column I want as SystemCol. how do I loop the each in the set column? I would like to display a message box for each of the rows in the selected column that has a value. The area in the code with the ** is where I am trying to insert the code but what I get is the full column address not a single cell address.
'===============================================================================================
'Description: Loops through the selected site and adds in the vulnerability totals for each _
systems
'Originally written by: Troy Pilewski
'Date: 2016-06-30
'===============================================================================================
'Declares variables
Dim ToWorkbook As Workbook, FromWorkbook As Workbook
Dim ToWorksheet As Worksheet, FromWorkSheet As Worksheet
Dim WorkingRange As Range, WholeRange As Range
Dim FromWorkbookVarient As Variant, ShipNameList() As Variant
Dim TitleString As String, FilterName As String, CurrentSystemName As String, _
ShipNames() As String, SelectedShipName As String
Dim LastRow As Long, ShipRow As Long
Dim StartRow As Integer
Const RowMultiplyer As Integer = 47
'-----------------------------------------------------------------------------------------------
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Set ToWorkbook = ActiveWorkbook
Set ToWorksheet = ToWorkbook.ActiveSheet
LastRow = ToWorksheet.Range("Y:Y").Find( _
What:="*", _
After:=ToWorksheet.Range("Y1"), _
LookAt:=xlByRows, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious _
).Row
'MsgBox _
' Prompt:="Y1:Y" & LastRow, _
' Title:="Ship Range"
ShipNameList = ToWorksheet.Range("Y1:Y" & LastRow).Value
For Each Item In ShipNameList
Dim BoundCounter As Integer
If Left(Item, 3) = "USS" Then
BoundCounter = BoundCounter + 1
End If
Next Item
ReDim ShipNames(BoundCounter - 1)
BoundCounter = 0
For Each Item In ShipNameList
If Left(Item, 3) = "USS" Then
ShipNames(BoundCounter) = Item
' Debug.Print ShipNames(BoundCounter)
BoundCounter = BoundCoutner + 1
Else
' Debug.Print UBound(ShipNames())
Exit For
End If
Next Item
TitleString = "Select a ship..."
SelectedShipName = GetChoiceFromChooserForm(ShipNames, TitleString)
If SelectedShipName = "" Then
Exit Sub
End If
ShipRow = ToWorksheet.Range("Y:Y").Find( _
What:=SelectedShipName, _
After:=ToWorksheet.Range("Y1"), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=True _
).Row
'Debug.Print ShipRow
StartRow = 14
If ShipRow > 1 Then
StartRow = (RowMultiplyer * (ShipRow - 1)) + StartRow
Else
StartRow = 14
End If
Set WorkingRange = ToWorksheet.Range("B" & StartRow & ":G" & StartRow + 38)
Set SystemCol = WorkingRange.Columns(2)
'Debug.Print WorkingRange.Address
FilterName = "Excel Files (*.xls), *.xls,Excel Files (*.xlsx), *.xlsx,All Files (*.*), *.*"
TitleString = "Scan File Selection"
**For Each rw In SystemCol
Debug.Print rw.Address
Next rw**
You'd be very well served to add Option Explicit to the top of your code modules to always ensure all variables must be declared.
You never declared SystemCol as a Range, nor rw as Range.
Following that adding .Cells to SystemCol in the loop ensures that you will loop through each individual cell in SystemCol. See below.
For Each rw In SystemCol.Cells
Debug.Print rw.Address
Next rw

Copy from a range and past in another sheet in the next empty cell in a row

I would like to have some tips to start a VBA code:
I have 2 sheets. Each row of the sheet(2) has text in each cells but between them it can have some empty cell.
My goal is to copy start from the row1 of sheet(2) from A1 to E1 and past it in the sheet(1) row 1 but without empty cell between them.
I edit my post because i did not thought about this important details. I would like to erase any duplicate in the same row but to keep the first entry.
And repeat the operation until the last row.
Data exemple:
Worksheet(2):
row1 cell1, cell2, cell3,cell4,cell5:
**ABC**, ,DEF,**ABC**,GHI
row(2) cell1, cell2, cell3,cell4,cell5:
ZZZ, , , ,YEU
Resultat expected:
Worksheet(1):
row1 cell1, cell2, cell3,cell4,cell5:
**ABC**,DEF,GHI, , ,
row(2) cell1, cell2, cell3,cell4,cell5:
ZZZ,YEU, , ,
Thank you for your help in advance!
Try this:
Sub stack_overflow()
Dim lngLastRow As Long
Dim xNum As Long
Dim xCell As Range
Dim shtFrom As Worksheet
Dim shtTo As Worksheet
Dim lngColCount As Long
'Change the two lines below this to change which sheets you're working with
Set shtFrom = ActiveWorkbook.Sheets(2)
Set shtTo = ActiveWorkbook.Sheets(1)
lngLastRow = shtFrom.Cells.Find(What:="*", After:=Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
For xNum = 1 To lngLastRow
lngColCount = 1
For Each xCell In shtFrom.Range("A" & xNum & ":E" & xNum)
If xCell.Value <> "" Then
If shtTo.Range("A" & xNum & ":E" & xNum).Find(What:=xCell.Value, LookIn:=xlValues, Lookat:=xlWhole) Is Nothing Then
shtTo.Cells(xNum, lngColCount).Value = xCell.Value
lngColCount = lngColCount + 1
End If
End If
Next xCell
Next xNum
End Sub
I found it:
Sub M()
lastrow = Sheets("Sheet2").Range("A1").SpecialCells(xlCellTypeLastCell).Row
For i = 1 To lastrow
Sheets("Sheet2").Range("A" & i & ": M" & i).Copy Sheets("Sheet1").Range("A" & i) ' Change Column M as required
Sheets("Sheet1").Range("A" & i & ": M" & i).SpecialCells(xlCellTypeBlanks).Delete Shift:=xlToLeft
Next
End Sub
You are going to have to provide some string manipulation after collecting the values from each row in order to remove the blanks.
Sub contract_and_copy()
Dim rw As Long, lr As Long, lc As Long, ws As Worksheet
Dim sVALs As String, vVALs As Variant
Set ws = Sheets("Sheet1")
With Sheets("Sheet2")
lr = .Cells.Find(what:=Chr(42), after:=.Cells(1, 1), SearchDirection:=xlPrevious).Row
For rw = 1 To lr
If CBool(Application.CountA(Rows(rw))) Then
vVALs = .Cells(rw, 1).Resize(1, .Cells(rw, Columns.Count).End(xlToLeft).Column).Value
sVALs = ChrW(8203) & Join(Application.Index(vVALs, 1, 0), ChrW(8203)) & ChrW(8203)
Do While CBool(InStr(1, sVALs, ChrW(8203) & ChrW(8203)))
sVALs = Replace(sVALs, ChrW(8203) & ChrW(8203), ChrW(8203))
Loop
sVALs = Mid(sVALs, 2, Len(sVALs) - 2)
vVALs = Split(sVALs, ChrW(8203))
ws.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Resize(1, UBound(vVALs) + 1) = vVALs
End If
Next rw
'Debug.Print lr
End With
End Sub
I've used a zero-length space as the delimiter as it is usually unlikely to be a part of a user's data.
You can try below approach also...
Public Sub remove_blank()
Dim arrayValue() As Variant
ThisWorkbook.Sheets("Sheet1").Activate ' Sheet1 has the data with blanks
arrayValue = range("A1:H2") ' Range where the data present...
Dim i As Long
Dim j As Long
Dim x As Integer: x = 1
Dim y As Integer: y = 1
For i = 1 To UBound(arrayValue, 1)
For j = 1 To UBound(arrayValue, 2)
Dim sStr As String: sStr = arrayValue(i, j)
If (Len(Trim(sStr)) <> 0) Then
ThisWorkbook.Sheets("Sheet2").Cells(x, y).Value = sStr ' Sheet2 is the destination
y = y + 1
End If
Next j
x = x + 1
y = 1
Next i
End Sub

Count all rows in a column

I am searching for a column in vba that has a certain header and then when I find that I want to search all the rows in that column and replace all the X's with 1's. I have all the code written but for some reason its not allowing the line shown below:
r2 = Range(i, i).EntireColumn.Rows.Count
Sub PA_Change()
Dim i As Long, r As Range, rRow As Range, r2 As Long
Set r = Range("A1")
Set rRow = r.EntireRow
For i = 1 To rRow.Columns.Count
If Cells(1, i) = PA_REQUIRED Then
r2 = Range(i, i).EntireColumn.Rows.Count
For j = 1 To r2
If Cells(j, i).Value = "X" Then
Cells(j, i).Value = "1"
End If
Next j
End If
Next i
End Sub
Try replacing all your code with this and let us know if that works:
*replace the "boo" in searchFor with the actual header name / PA_REQUIRED
Sub PA_Change()
Dim searchFor As String
searchFor = "boo"
Dim grabColumn As Range
Set grabColumn = Rows("1:1").Find(What:=searchFor, _
After:=Range("A1"), LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, MatchCase:=False, _
SearchFormat:=False)
If Not grabColumn Is Nothing Then
Dim entireColumn As Range
Set entireColumn = Range(grabColumn.Address & ":" & Split(grabColumn.Address, "$")(1) & Range(Split(grabColumn.Address, "$")(1) & Rows.Count).End(xlUp).Row)
Dim cell As Range
For Each cell In entireColumn
If UCase(cell) = "X" Then
cell = "1"
End If
Next
Else
Exit Sub ' not found
End If
End Sub