How to auto fill column down in VBA - excel

How can I auto fill a column down, e.g., Cell(A1).value = dog and Cell(A12).value = Pen
How do I fill down A2:A11 with the value = dog and the A13 value = pen without manually selecting the column?
Sub filldown_example()
Dim missingcells as range
Dim fillsedcells as range
Set missingcells = select
For each filledcells in missingcells
If filledcells = "" Then
filledcells.filldown
End If
Next filledcells
End sub

No need to loop here.
Sub fillit()
With Range("a1:a13")
.SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
.Value = .Value 'formula to value
End With
End Sub

You do not need VBA for this. If you search Google for Excel fill all blanks with cell above you will get the non-VBA method.
If you still want VBA, then try this. You do not need to loop through all cells.
Code:
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long
Dim rng As Range
'~~> Change this to the relevant sheet
Set ws = Sheet1
With ws
'~~> Find the last row in Col A and add 1 to it
lRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
'~~> Find the blank cells
On Error Resume Next
Set rng = .Range("A1:A" & lRow).SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
'~~> Add the formula to get values from the above cell in 1 go
If Not rng Is Nothing Then rng.FormulaR1C1 = "=R[-1]C"
'~~> Convert formulas to values
.Range("A1:A" & lRow).Value = .Range("A1:A" & lRow).Value
End With
End Sub
In Action:

Fill Down Selection
This will allow you to select multiple ranges with multiple columns to fill down each of them.
Range
Sub FillDownSelectionRange()
If TypeName(Selection) <> "Range" Then Exit Sub
Dim rg As Range: Set rg = Selection
Dim arg As Range ' Area Range
Dim crg As Range ' Column Range
Dim rCell As Range ' Row Cell Range
Dim rValue As Variant
For Each arg In rg.Areas
For Each crg In arg.Columns
If crg.Rows.Count > 1 Then
For Each rCell In crg.Cells
If Len(CStr(rCell.Value)) = 0 Then
rCell.Value = rValue
Else
If rCell.Value <> rValue Then
rValue = rCell.Value
End If
End If
Next rCell
End If
rValue = Empty
Next crg
Next arg
End Sub
Array
To speed up, instead of looping through the cells, you could loop through an array.
Sub FillDownSelectionArray()
If TypeName(Selection) <> "Range" Then Exit Sub
Dim rg As Range: Set rg = Selection
Dim arg As Range ' Area Range
Dim crg As Range ' Column Range
Dim cData As Variant ' Column Array
Dim rValue As Variant
Dim r As Long
For Each arg In rg.Areas
For Each crg In arg.Columns
If crg.Rows.Count > 1 Then
cData = crg.Value
For r = 1 To UBound(cData, 1)
If IsEmpty(cData(r, 1)) Then
cData(r, 1) = rValue
Else
If cData(r, 1) <> rValue Then
rValue = cData(r, 1)
End If
End If
Next r
crg.Value = cData
End If
rValue = Empty
Next crg
Next arg
End Sub

Related

vba select only cells with value in range

I want to select only cells that contains data in specific range (C7:I15). Code below can do that only for column "G". How to change code for my range?
Sub Testa()
Dim LR As Long, cell As Range, rng As Range
With Sheets("Sheet1")
LR = .Range("G" & Rows.Count).End(xlUp).Row
For Each cell In .Range("G2:G" & LR)
If cell.Value <> "" Then
If rng Is Nothing Then
Set rng = cell
Else
Set rng = Union(rng, cell)
End If
End If
Next cell
rng.Select
End With
End Sub
You can use a generic function to which you pass the range that should be checked - and which returns a range with the non-empty cells (see update below for function using SpecialCells instead of iteration)
Public Function rgCellsWithContent(rgToCheck As Range) As Range
Dim cell As Range
For Each cell In rgToCheck
If cell.Value <> "" Then
If rgCellsWithContent Is Nothing Then
Set rgCellsWithContent = cell
Else
Set rgCellsWithContent = Union(rgCellsWithContent, cell)
End If
End If
Next cell
End Function
You can use this sub like this:
Sub Testa()
With ThisWorkbook.Worksheets("Sheet1")
'select cells in range C7:I15
rgCellsWithContent(.Range("C7:I15")).Select
'select cells in column G
Dim LR As Long
LR = .Range("G" & Rows.Count).End(xlUp).Row
rgCellsWithContent(.Range("G2:G" & LR)).Select
'you can even combine both
Dim rgNew As Range
Set rgNew = rgCellsWithContent(.Range("C7:I15"))
Set rgNew = Union(rgNew, rgCellsWithContent(.Range("G2:G" & LR)))
rgNew.Select
End With
End Sub
UPDATE:
This function uses the SpecialCells command.
You can make a difference to return values only or to return values and formulas.
Public Function rgCellsWithContent(rgToCheck As Range, _
Optional fValuesAndFormulas As Boolean = True) As Range
Dim cell As Range
On Error Resume Next 'in case there are no cells
With rgToCheck
Set rgCellsWithContent = .SpecialCells(xlCellTypeConstants)
If fValuesAndFormulas Then
Set rgCellsWithContent = Union(rgCellsWithContent, .SpecialCells(xlCellTypeFormulas))
End If
End With
On Error GoTo 0
End Function
If no formulas in the range where selection should be done, you can use the next compact code, not needing any iteration:
Dim rng As Range
On Error Resume Next 'for the case of no any empty cell
Set rng = Range("C7:I15").SpecialCells(xlCellTypeConstants)
On Error GoTo 0
If Not rng Is Nothing Then rng.Select
The next version is able to deal with formulas, too:
Dim rng As Range, rngSel As Range, arrFormula
Set rng = Range("C7:I15")
With rng
arrFormula = .Formula
.Value = .Value
On Error Resume Next
Set rngSel = .SpecialCells(xlCellTypeConstants)
On Error GoTo 0
.Formula = arrFormula
End With
If Not rngSel Is Nothing Then rngSel.Select

VBA create names for columns

So I have a worksheet called "gar_nv" containing in its first row some strings that I'd like to define as names for my columns. For instance, first cell of column A is "Number", I'd like to refer to the column A(starting from the second cell) as "Number" instead of column "A".
Sub NameCol()
Dim LastRow As Long
Dim x As Long, Rng As Range
With gar_nv
For x = 1 To .UsedRange.Columns.Count
LastRow = Cells(Cells.Rows.Count, x).End(xlUp).Row
Set Rng = Cells(2, x).Resize(LastRow)
.Names.Add Name:=Cells(1, x), RefersTo:=Rng
Set Rng = Nothing
Next
End With
End Sub
When I test my code like this, it throws a 91 error, what am I doing wrong?
Sub test()
With gar_nv
For Each Rng In .Range("Number")
MsgBox (Rng.Value)
Next
End With
End Sub
Create Names for Columns of Data
gar_nv is the code name of a worksheet in the workbook containing this code.
Option Explicit
Sub NameColumnsData()
' Delete all previous names in the worksheet.
'DeleteAllWorksheetNames gar_nv
Dim hrg As Range ' header range
Dim drg As Range ' data range
Dim cCount As Long ' number of columns
With gar_nv.UsedRange
Set hrg = .Rows(1)
Set drg = .Resize(.Rows.Count - 1).Offset(1)
cCount = .Columns.Count
End With
Dim crg As Range
Dim c As Long
Dim cTitle As String
For c = 1 To cCount
cTitle = hrg.Cells(c).Value
Set crg = drg.Columns(c)
gar_nv.Names.Add cTitle, crg
' Of course, you can lose the variables and just do:
'gar_nv.Names.Add hrg.Cells(c).Value, drg.Columns(c)
Next c
MsgBox "Column data names created.", vbInformation
End Sub
Sub NameColumnsDataTEST()
Dim cCell As Range
With gar_nv
For Each cCell In .Range("Number").Cells
' Caution! If there are many cells it may take 'forever'.
'MsgBox cCell.Address(0, 0) & ": " & cCell.Value
' Rather print to the Immediate window (Ctrl+G):
Debug.Print cCell.Address(0, 0) & ": " & cCell.Value
Next
End With
End Sub
Sub DeleteAllWorksheetNames(ByVal ws As Worksheet)
Dim nm As Name
For Each nm In ws.Names
Debug.Print nm.Name, nm.RefersTo, "Deleted!"
nm.Delete
Next nm
End Sub

CountIf Function does not count in Excel VBA

in Every cell in a Selection I need to replace a combination of characters. But before a replacement I want to count every combination.
The problem is that CountIf function won't count at all. Though replacement performs.
Here is a code:
Option Explicit
Sub sdfsdf()
Dim rng As Range
Dim i As Integer
Dim Cell As Range
Dim Counter As Variant
'Application.Run "Personal.xlsb!Main"
Set rng = Selection
Set Cell = Cell
Let i = 1
For Each Cell In Selection
Let Counter = Application.WorksheetFunction.CountIf(Cell, i & "&")
Cell.Replace What:=i & "&", Replacement:=""
i = i + 1
Next Cell
MsgBox Counter
End Sub
A snippet of a column with combinations to delete. I need to delete 1&, 2&, 3& and so on.
Remove Characters
Sub RemoveChars()
Const Criteria As String = "&"
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range
Set rg = ws.Range("U2", ws.Cells(ws.Rows.Count, "U").End(xlUp))
' Or:
'Set rg = Selection
Dim Cell As Range
Dim Position As Long
Dim cCount As Long
Dim cString As String
For Each Cell In rg.Cells
cString = CStr(Cell.Value)
Position = InStr(1, cString, Criteria)
If Position > 0 Then ' criteria found; replace with chars to the right
Cell.Value = Mid(cString, Position + 1, Len(cString) - Position)
cCount = cCount + 1
'Else ' criteria not found; do nothing
End If
Next Cell
MsgBox "Cells processed: " & cCount, vbInformation
End Sub

VBA nested loop - outer loop doesnt jump to next value

I'm trying to do a macro to produce a list based on a lookup list. For some reason the outer loop doesnt work, it only iterates once.
Sub Macro5()
Dim LookupRng As Range
Dim Store As String
Dim jrow As Integer
Dim irow As Integer
Dim i As Integer
Dim j As Integer
Set LookupRng = Sheet1.Range("B2") ' The Lookup range
jrow = Sheet2.Range("T" & Rows.Count).End(xlUp).Row ' last row of list of values to be searched
irow = Sheet1.Range("B" & Rows.Count).End(xlUp).Row ' last row of lookup range
Sheet3.Range("A2:A" & Rows.Count).Clear
For j = 2 To jrow
Store = Sheet2.Cells(j, 20).Value ' the value to be searched in the lookup range
For i = 1 To irow
If LookupRng.Value = Store Then
Sheet3.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = LookupRng.Offset(0, -1).Value
End If
Set LookupRng = LookupRng.Offset(1, 0)
Next i
Next j
The i loop works, it searches all values that match "Store", but for some reason the j loop doesnt seem to be working, it doesnt jump to the next value of the "Store" list.
I'm new to this so would prefer a simple solution, but any help will be very appreciated
Loops with Offset
The issue was that you are not resetting the Lookup Range to the initial position after each inner loop finishes. So the following loops were trying to compare the values below the Lookup Range which were empty.
You should abandon the idea of 'offsetting' and use Cells or Range and increase the rows or define the range and use a For Each loop. But the best would be to use only one loop and use Application.Match to find a match.
A Quick Fix
Option Explicit
Sub lookupLoop()
Dim ilCell As Range ' Initial Lookup Cell Range
Dim lCell As Range ' Lookup Cell Range
Dim dCell As Range ' Destination Cell Range
Dim Store As Variant ' Current Value in Search Range
Dim iRow As Long ' Last Row of Lookup Range
Dim jRow As Long ' Last Row of Search Range
Dim i As Long ' Lookup Range Rows Counter
Dim j As Long ' Search Range Rows Counter
Set ilCell = Sheet1.Range("B2")
iRow = Sheet1.Range("B" & Sheet1.Rows.Count).End(xlUp).Row
jRow = Sheet2.Range("T" & Sheet2.Rows.Count).End(xlUp).Row
Sheet3.Range("A2:A" & Sheet3.Rows.Count).Clear
Set dCell = Sheet3.Range("A2")
For j = 2 To jRow
Store = Sheet2.Cells(j, "T").Value
Set lCell = ilCell '***
For i = 2 To iRow
If lCell.Value = Store Then
dCell.Value = lCell.Offset(0, -1).Value
Set dCell = dCell.Offset(1)
Exit For
End If
Set lCell = lCell.Offset(1)
Next i
Next j
End Sub
An Application.Match solution might look like this:
Sub lookupAM()
Dim lrg As Range ' Lookup Range (Read)
Dim vrg As Range ' Values Range (Write)
Dim srg As Range ' Search Range
Dim sCell As Range ' Current Cell in Search Range
Dim dCell As Range ' Current Cell in Destination Range
Dim cMatch As Variant ' Current Match
Dim lRow As Long ' Last Row of Lookup Range
Dim sRow As Long ' Last Row of Search Range
lRow = Sheet1.Range("B" & Sheet1.Rows.Count).End(xlUp).Row
Set lrg = Sheet1.Range("B2:B" & lRow)
Set vrg = lrg.Offset(, -1)
sRow = Sheet2.Range("T" & Sheet2.Rows.Count).End(xlUp).Row
Set srg = Sheet2.Range("T2:T" & sRow)
Sheet3.Range("A2:A" & Sheet3.Rows.Count).Clear
Set dCell = Sheet3.Range("A2")
For Each sCell In srg.Cells
cMatch = Application.Match(sCell.Value, lrg, 0)
If IsNumeric(cMatch) Then
dCell.Value = vrg.Cells(cMatch).Value
End If
Set dCell = dCell.Offset(1)
Next sCell
End Sub

Finding column based on header then formatting rows

I am attempting to build a loop that searches through headers and finds a contained value, In this case, "Avg". If the value is found it will work down the column and apply a format based on a comparison to another column. I am trying to convert my cell variable in the For loop (Z) into a column address so I can use to control my ws.Cells() value in the next loop.
Any help is greatly appreciated, thanks!!!!
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
Dim Z As Range
lastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
For Each Z In Range("I1:BM1").Cells
If InStr(1, Z.Value, "Avg") Then
For i = 2 To lastRow 'loop from row 2 to last
If ws.Cells(i, 8) - ws.Cells(i, Z) < 0 Then
ws.Cells(i, Z).Interior.ColorIndex = 4
End If
Next i
End If
Next Z
End Sub
It's not exactly clear to me what you want - but from the title it appears you want to get the column number based on the header text? If so, this will do that:
Private Function GetColumn(headerName As String) As Integer
Dim col As Integer
GetColumn = 0
For col = 1 To ActiveSheet.UsedRange.Columns.Count
If ActiveSheet.Cells(1, col).Value = headerName Then
GetColumn = col
Exit For
End If
Next col
End Function
Find Header and Format Cells
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: In a column range specified by its header,
' highlights the cells matching a condition.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub HighlightBelowAverages()
' Define constants.
Const PROC_TITLE As String = "Highlight Below-Averages"
Const COMPARE_COLUMN As String = "H"
Const AVG_SEARCH_COLUMNS As String = "I:BM"
Const AVG_COLUMN_HEADER As String = "Avg"
Const AVG_COLOR_INDEX As Long = 4 ' Bright Green
' Reference the Search range.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets("Sheet1")
Dim srg As Range
' It is NOT assumed that the used range starts in row '1'.
Set srg = Intersect(ws.UsedRange, ws.Range(AVG_SEARCH_COLUMNS))
If srg Is Nothing Then
MsgBox "The Average search columns '" & AVG_SEARCH_COLUMNS _
& "' are not part of the used range.", vbCritical, PROC_TITLE
Exit Sub
End If
' Find the Average header cell.
Dim ahCell As Range
With srg
Set ahCell = .Find(AVG_COLUMN_HEADER, _
.Cells(.Rows.Count, .Columns.Count), xlFormulas, xlWhole, xlByRows)
End With
If ahCell Is Nothing Then
MsgBox "Header '" & AVG_COLUMN_HEADER & "' not found.", _
vbCritical, PROC_TITLE
Exit Sub
End If
' Reference the Average (single-column) range.
Dim afCell As Range: Set afCell = ahCell.Offset(1)
Dim alCell As Range
Set alCell = Intersect(srg.Rows(srg.Rows.Count), ws.Columns(ahCell.Column))
' It IS assumed that the data has one row of headers.
If afCell.Row > alCell.Row Then
MsgBox "No data found.", vbCritical, PROC_TITLE
Exit Sub
End If
Dim arg As Range: Set arg = ws.Range(afCell, alCell)
' Reference the Compare (single-column) range.
Dim crg As Range
' It is NOT assumed that the used range starts in column 'A'.
Set crg = Intersect(arg.EntireRow, ws.Columns(COMPARE_COLUMN))
' Highlight the cells.
Application.ScreenUpdating = False
arg.Interior.ColorIndex = xlNone
Dim aCell As Range, cCell As Range, r As Long
For Each aCell In arg.Cells
r = r + 1
Set cCell = crg.Cells(r)
If cCell.Value < aCell.Value Then ' Compare is less than Average
aCell.Interior.ColorIndex = AVG_COLOR_INDEX
End If
Next aCell
Application.ScreenUpdating = True
' Inform.
MsgBox "Below-averages highlighted.", vbInformation, PROC_TITLE
End Sub

Resources