UDF to delete or move all columns not in array - excel

I am trying to write a UDF that will Move or Delete all columns in an array
This part I have.
I also want to Delete or Move all columns not in the array and this part I am stuck on.
This is Case 2 and 4 of the function
Any help on this is appreciated
Thanks
Edit: Updated with answer provided by David G
Function InvertRng(shtName As String, r As Range) As Range
Dim rng As Range
Dim Rng1 As Range, Rng2 As Range
Set Rng1 = GetUsedRange(shtName, 1, True)
For Each rng In Rng1
If Application.Intersect(rng, r) Is Nothing Then
If InvertRng Is Nothing Then
Set InvertRng = rng
Else
Set InvertRng = Application.Union(InvertRng, rng)
End If
End If
Next
End Function
Original Question
Sub MoveOrDelete_n()
MoveOrDelete 2, "Elements", "NewSheet", Array("Id", "Type", "Description")
End Sub
The Function
Function MoveOrDelete(iwhat As Long, SshtName As String, TshtName As String, arrHeaders As Variant) 'Excel VBA to move Columns based on criteria
Dim wsS As Worksheet, wsT As Worksheet
Dim ar As Variant
Dim fn As Range, r As Range
Dim str As String
Dim i As Long
Set wsS = ThisWorkbook.Sheets(SshtName)
Set wsT = ThisWorkbook.Sheets(TshtName)
For i = 0 To UBound(arrHeaders) 'Loop through the Array
Set fn = wsS.Rows("1:1").Find(arrHeaders(i), LookAt:=xlWhole)
str = str & fn.Address & ","
Next i
'Remove the trailing comma from the string
str = Left(str, Len(str) - 1)
Set r = wsS.Range(str).EntireColumn
Select Case iwhat
Case 1
'Delete all columns IN list
r.Delete
Case 2
'Delete all columns NOT in list
invertR.Delete
Case 3
'Move all columns IN List to NEW Sheet
r.Copy wsT.[a1]
Case 4
'Move all columns NOT in List to NEW SheeT
invertR.Copy wsT.[a1]
End Select
End Function

I found this function that invert the selection, maybe is what you need:
Sub InvertSelection()
'Updateby20140314
Dim rng As Range
Dim Rng1 As Range
Dim Rng2 As Range
Dim OutRng As Range
xTitleId = "KutoolsforExcel"
Set Rng1 = Application.Selection
Set Rng1 = Application.InputBox("Range1 :", xTitleId, Rng1.Address, Type:=8)
Set Rng2 = Application.InputBox("Range2", xTitleId, Type:=8)
For Each rng In Rng2
If Application.Intersect(rng, Rng1) Is Nothing Then
If OutRng Is Nothing Then
Set OutRng = rng
Else
Set OutRng = Application.Union(OutRng, rng)
End If
End If
Next
OutRng.Select
End Sub
https://www.extendoffice.com/documents/excel/762-excel-reverse-selections.html

Related

Bulk find and replace same values of in cells of a column

I am trying to create an Excel VBA function to search values of column A and find same cells in column H, and replace these cells in B2:F6 with values of J2:N4.
My Input File:
Desired Output:
I have tried the following VBA code but it doesn't work. it finds and replace the values of column Replace1 and ignores Replace 2,3,... .
Sub MultiFindNReplace()
'Update 20140722
Dim Rng As Range
Dim InputRng As Range, ReplaceRng As Range
xTitleId = "KutoolsforExcel"
Set InputRng = Application.Selection
Set InputRng = Application.InputBox("Original Range ", xTitleId, InputRng.Address, Type:=8)
Set ReplaceRng = Application.InputBox("Replace Range :", xTitleId, Type:=8)
Application.ScreenUpdating = False
For Each Rng In ReplaceRng.Columns(1).Cells
InputRng.Replace what:=Rng.Value, replacement:=Rng.Offset(0, 1).Value
Next
Application.ScreenUpdating = True
End Sub
Looks like both datasets got same headers so you can benefit from that. If the headers are always the same and same sorting, just copy whole row:
Sub test()
'if headers of both datasets are always the same and sorted the same way, just copy whole row
Dim rngDestiny As Range
Dim rngSource As Range
Dim rngFind As Range
Dim rng As Range
Dim i As Long
Dim RowN As Long
Dim LR As Long
Set rngSource = Range("I2:M4")
Set rngFind = Range("H2:H4")
Set rngDestiny = Range("B2:F6")
LR = Range("A" & Rows.Count).End(xlUp).Row 'last non-blank cell in column f-name
For i = 2 To LR Step 1
With Application.WorksheetFunction
'check if the value of f-name exists in column FIND
If .CountIf(rngFind, Range("A" & i).Value) > 0 Then
'there is a match, get row number and copy
RowN = .Match(Range("A" & i).Value, rngFind, 0)
rngSource.Rows(RowN).Copy rngDestiny.Rows(i - 1) 'minus 1 because our first row of data starts with i=2!!!
End If
End With
Next i
Set rngSource = Nothing
Set rngFind = Nothing
Set rngDestiny = Nothing
End Sub

i want to find and hightlight a specific word in a excel, but the whole cell is getting highlighted pls help me

Sub Sample()
Dim fnd As String
Dim MyAr
Dim i As Long
Dim rng As Range, FoundCell As Range, LastCell As Range, myRange As Range
Set myRange = ActiveSheet.UsedRange
Set LastCell = myRange.Cells(myRange.Cells.Count)
fnd = "university/checklist"
MyAr = Split(fnd, "/")
For i = LBound(MyAr) To UBound(MyAr)
Set FoundCell = myRange.Find(what:=MyAr(i), after:=LastCell)
If Not FoundCell Is Nothing Then
FirstFound = FoundCell.Address
End If
Set rng = FoundCell
Do Until FoundCell Is Nothing
Set FoundCell = myRange.FindNext(after:=FoundCell)
Set rng = Union(rng, FoundCell)
If FoundCell.Address = FirstFound Then Exit Do
Loop
If Not rng Is Nothing Then
rng.Characters.Font.ColorIndex = 3
End If
Next i
End Sub
Highlight Strings in Cells
Option Explicit
Sub HighlightStrings()
Const CriteriaList As String = "university,checklist"
Const CriteriaColor As Long = vbRed
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range: Set rg = ws.UsedRange
Dim Criteria() As String: Criteria = Split(CriteriaList, ",")
Dim c As Long
Dim cLen As Long
Dim cString As String
Dim fCell As Range
Dim fArr() As String
Dim f As Long
Dim fPos As Long
Dim fString As String
Dim fFirstAddress As String
For c = 0 To UBound(Criteria)
cString = Criteria(c)
cLen = Len(cString)
Set fCell = rg.Find(cString, , xlFormulas, xlPart)
If Not fCell Is Nothing Then
fFirstAddress = fCell.Address
Do
fString = fCell.Value
fPos = 1
fArr = Split(fString, cString, , vbTextCompare)
For f = 0 To UBound(fArr) - 1
fPos = fPos + Len(fArr(f))
fCell.Characters(fPos, cLen).Font.Color = CriteriaColor
fPos = fPos + cLen
Next f
Set fCell = rg.FindNext(fCell)
Loop Until fCell.Address = fFirstAddress
End If
Next c
MsgBox "Criteria strings highlighted.", vbInformation
End Sub
Please, try the next updated code. As I said in my above comment you cannot use a Union range for what you try doing, because you need to search for each cell and find the appropriate cell characters to be colored. You can iterate between such a range again but nothing will be gain:
Dim fnd As String, FirstFound As String, MyAr, i As Long, pos As Long
Dim FoundCell As Range, LastCell As Range, myRange As Range
Set myRange = ActiveSheet.UsedRange
Set LastCell = myRange.cells(myRange.cells.count)
fnd = "university/checklist"
MyAr = Split(fnd, "/")
For i = LBound(MyAr) To UBound(MyAr)
Set FoundCell = myRange.Find(MyAr(i)), , , xlPart
If Not FoundCell Is Nothing Then
FirstFound = FoundCell.Address
pos = InStr(1, FoundCell.Value, MyAr(i), vbTextCompare)
FoundCell.Characters(pos, Len(MyAr(i))).Font.ColorIndex = 3
Do
Set FoundCell = myRange.FindNext(FoundCell)
pos = InStr(1, FoundCell.Value, MyAr(i))
FoundCell.Characters(pos, Len(MyAr(i))).Font.ColorIndex = 3
Loop While FoundCell.Address <> FirstFound
End If
Next i
End Sub
If the range to be processed is large, you should use some optimization lines as Application.ScreenUpdating = False, Application.EnableEvents = False and
Application.Calculation = xlManual at the beginning of the code and ending with Application.ScreenUpdating = False, Application.EnableEvents = False and
Application.Calculation = xlCalculationAutomatic
First of, try to tidy a bit your code example, it's quite messy hence I am not sure of what you are actually trying to achieve. Some accompanying comments would help also
Anyway, one thing is for sure :
rng.Characters.Font.ColorIndex = 3
will attribute a color to the whole text in the selected range. To specify only a subset of the text in the range, you need to use :
rng.Characters(Start:=x, Length:=y).Font.ColorIndex = 3
Where x being the starting character and Length being the length that you want to turn into the given font color.
You can find the start value and length using
start = InStr(1, rng, MyAr(i))
length = len(MyAr(i))
Which will lead to the following line
rng.Characters(Start:=start, Length:=length).Font.ColorIndex = 3
And as specified by FaneDuru, it should be done on a cell by cell basis. Either you do it instead of the Union, or you look on the cells within the rng.
for cell in rng.Cells
start = InStr(1, cell, MyAr(i))
...
next cell
Moreover, as described here, it will only color the first occurrence.
If the value you are looking for can appear several time, you either need an alternate way or set some iteration until there are no more matches by modifying the starting position in the InStr where 1 would become the last matched position + 1

Loop through the range VBA

Looking for a simple loop through the range (say column A range("A5:A15")) if there is a blank cell within that range I need the entire row/rows associated with the blank cell/cells to be hidden.
I was thinking of something like this to accommodate various ranges but get "type Mismatch" error. Any reasons why
Sub test()
Dim rng As Range, cell As Variant, ar As Variant
Dim Rng1 As Range, Rng2 As Range, Rng3 As Range, Rng4 As Range
Dim MyArray(1 To 4) As Range
With ThisWorkbook.Worksheets("sheet1")
'Set MyArray = rng
Set MyArray(1) = Range("O8:O17")
Set MyArray(2) = Range("O55:O64")
Set MyArray(3) = Range("G37:G46")
Set MyArray(4) = Range("G89:G98")
'ar = Array(Rng1, Rng2, Rng3, Rng4)
'Set rng = .Range("O8:O17")
For Each cell In MyArray
If Len(cell.Value) < 1 Then
cell.EntireRow.Hidden = True
End If
Next cell
End With
End Sub
?
Something Like this:
You can put it in a subject:
For Each cell In Range("A5:A15")
If Len(cell.Value) < 1 Then
cell.EntireRow.Hidden = True
End If
Next
For Each cell In Range("A40:A55")
If Len(cell.Value) < 1 Then
cell.EntireRow.Hidden = True
End If
Next
New Answer :
Dim rng As Range, cell As Variant, ar As Variant
Dim Rng1 As Range, Rng2 As Range, Rng3 As Range, Rng4 As Range
Dim MyArray(1 To 4) As Range
With ThisWorkbook.Worksheets("sheet1")
'Set MyArray = rng
Set MyArray(1) = Range("O8:O17")
Set MyArray(2) = Range("O55:O64")
Set MyArray(3) = Range("G37:G46")
Set MyArray(4) = Range("G89:G98")
'ar = Array(Rng1, Rng2, Rng3, Rng4)
'Set rng = .Range("O8:O17")
Dim i As Integer
For i = LBound(MyArray) To UBound(MyArray)
For Each cell In MyArray(i)
If Len(cell.Value) < 1 Then
cell.EntireRow.Hidden = True
End If
Next
Next
End With
Try:
Option Explicit
Sub test()
Dim rng As Range, cell As Range
With ThisWorkbook.Worksheets("Sheet1")
Set rng = .Range("A5:A15")
For Each cell In rng
If cell.Value = "" Then
.Rows(cell.Row).EntireRow.Hidden = True
End If
Next cell
End With
End Sub
This takes full advantage of the Excel VBA model. I'm guessing it's faster than the above but have not conducted performance tests.
Dim Cell As Range
For Each Cell In Range("A5:A15").SpecialCells(xlCellTypeBlanks)
Cell.EntireRow.Hidden = True
Next
Try the following
Option Explicit
Sub youcouldhaveatleasttriedtodosomethingyourself()
Dim r1 As Range, r2 As Range, c As Range, target As Range
With Workbooks(REF).Sheets(REF)
Set r1 = .Range("A1:A54")
Set r2 = .Range("F3:F32")
Set target = Application.Union(r1, r2)
For Each area In target.Areas
For Each c In area
If c.Value = vbNullString Then .Rows(c.Row).EntireRow.Hidden = True
Next c
Next area
End With
End Sub
Please note that I now have set two exemplifying ranges. You can always add more range variables to the Union function.

VBA Multiple value find and replace but also highlight replaced cells

I have a code that finds and replaces values in one sheet from a list in another sheet. However, I need this code to also highlight the cell, or flag it in some way so that it can be reviewed manually later. Any suggestions?
Here is the code:
Sub ReplaceValues()
Dim FandR As Worksheet
Dim PDH As Worksheet
Dim rng As Range, rngR As Range
Dim i As Long
Dim rngReplacement
Dim c As Range
Dim curVal As String
Set FandR = Sheets("Find and Replace")
Set PDH = ThisWorkbook.Sheets("Paste Data here")
i = PDH.Rows.Count
With PDH
Set rng = .Range("E1", .Range("E" & i).End(xlUp))
End With
With FandR
Set rngR = FandR.Range("H")
End With
For Each c In rngR
curVal = c.Value
c.Interior.Color = vbYellow
With rng
.Replace curVal, c.Offset(0, 1).Value, xlWhole, , True
End With
Next
End Sub

VBA Select Case with Named Range does not work

I am trying to insert a new row into a named range. The user selects a "category" from a combo box e.g., Cool Drinks, Beer and Cider, Bitters etc... and then the contents of that category populate another combo box.
I have named the ranges of all of the Categories and would like them to populate the second combo box. I have a code which works by itself:
Dim rng As Range
Dim DailySales As Worksheet
Set DailySales = Worksheets("Daily Sales")
Set rng = DailySales.Range("CoolDrinksDailySales")
For Each rng In DailySales.Range("CoolDrinksDailySales")
Me.CmboName.AddItem rng.Value
Next rng
However, whenever I try to use that in a Select Case, it doesn't work.
Dim rng As Range
Dim DailySales As Worksheet
Set DailySales = Worksheets("Daily Sales")
Select Case Me.CmboType.Value
Case "Cool Drinks"
Set rng = DailySales.Range("CoolDrinksDailySales")
For Each rng In DailySales.Range("CoolDrinksDailySales")
Me.CmboName.AddItem rng.Value
Next rng
Case "Beer and Cider"
Set rng = DailySales.Range("BeerCiderDailySales")
For Each rng In DailySales.Range("BeerCiderDailySales")
Me.CmboName.AddItem rng.Value
Next rng
End Select
Does anybody have any ideas?
Here is the complete code:
Option Explicit
Private Sub UserForm_Initialize()
'InitializeTypeCombo
Dim Types() As String
Types = Split("Cool Drinks,Beer and
Cider,Bitters,Brandy,Whiskey,Rum,Spirits,Sherry,White Wine,Red Wine",
",")
Dim i As Integer
For i = LBound(Types) To UBound(Types)
Me.CmboType.AddItem Types(i)
Next
'InitializeNameCombo
Dim rng As Range
Dim DailySales As Worksheet
Set DailySales = Worksheets("Daily Sales")
Select Case Me.CmboType.Value
Case "Cool Drinks"
Set rng = DailySales.Range("CoolDrinksDailySales")
For Each rng In DailySales.Range("CoolDrinksDailySales")
Me.CmboName.AddItem rng.Value
Next rng
Case "Beer and Cider"
Set rng = DailySales.Range("BeerCiderDailySales")
For Each rng In DailySales.Range("BeerCiderDailySales")
Me.CmboName.AddItem rng.Value
Next rng
End Select
End Sub
Private Sub CmdExit_Click()
Unload Me
End Sub
Private Sub CmdEnter_Click()
Dim rng As Range
'Store Date Index
Dim colArray(32) As Integer
'Store Item Index
Dim rowArray(150) As Integer
'Store first value for Find and FindNext
Dim FirstAddress As String
Dim i As Integer
Dim j As Integer
i = 0
j = 0
With Range("B6:AD6")
Set rng = .Find(TxtDate.Value, LookIn:=xlValues)
If Not rng Is Nothing Then
FirstAddress = rng.Address
Do
Set rng = .FindNext(rng)
colArray(i) = rng.Column
i = i + 1
Loop While Not rng Is Nothing And rng.Address <> FirstAddress
End If
End With
With Range("A7:A150")
Set rng = .Find(CmboName.Value, LookIn:=xlValues)
If Not rng Is Nothing Then
FirstAddress = rng.Address
Do
Set rng = .FindNext(rng)
rowArray(j) = rng.Row
j = j + 1
Loop While Not rng Is Nothing And rng.Address <> FirstAddress
End If
End With
Dim c As Integer
Dim r As Integer
For c = 0 To i - 1
For r = 0 To j - 1
Cells(rowArray(r), colArray(c)).Value = TxtNoSold.Value
Next r
Next c
Unload Me
End Sub
The solution was simply moving the Select Case into the Combobox_Change event. As Dick Kusleika said, the value of the combobox was nothing at runtime. Here is the correct code to accomplish what I was trying to do.
Option Explicit
Private Sub Userform_Initialize()
'Populate cmboTypes
Dim Types() As String
Types = Split("Cool Drinks,Beer and _
Cider,Bitters,Brandy,Whiskey,Rum,Spirits,Sherry,White Wine,_
Red Wine", ",")
'Loop through the values populated in the split function above, and add
'each item to the combobox
Dim i As Integer
For i = LBound(Types) To UBound(Types)
Me.CmboType.AddItem Types(i)
Next
End Sub
Sub CmboType_Change()
Dim rng As Range
Dim DailySales As Worksheet
'Populate CmboName with named dynamic ranges of "Types"
Set DailySales = Worksheets("Daily Sales")
Select Case Me.CmboType.Value
Case "Cool Drinks"
Set rng = DailySales.Range("CoolDrinksDailySales")
For Each rng In DailySales.Range("CoolDrinksDailySales")
Me.CmboName.AddItem rng.Value
Next rng
Case "Beer and Cider"
CmboName.Clear
Set rng = DailySales.Range("BeerCiderDailySales")
For Each rng In DailySales.Range("BeerCiderDailySales")
Me.CmboName.AddItem rng.Value
Next rng
Case "Bitters"
CmboName.Clear
Set rng = DailySales.Range("BittersDailySales")
For Each rng In DailySales.Range("BittersDailySales")
Me.CmboName.AddItem rng.Value
Next rng
Case "Brandy"
CmboName.Clear
Set rng = DailySales.Range("BrandyDailySales")
For Each rng In DailySales.Range("BrandyDailySales")
Me.CmboName.AddItem rng.Value
Next rng
Case "Whiskey"
CmboName.Clear
Set rng = DailySales.Range("WhiskeyDailySales")
For Each rng In DailySales.Range("WhiskeyDailySales")
Me.CmboName.AddItem rng.Value
Next rng
End Select
End Sub

Resources