I am working on my code and I have this to filter rows and insert a formula in the first filter row. with that formula I want it to fill down, but it only insert the formula in the first filtered row and doesn't fill down.
Sub Cal()
dim LastRow as long
With Worksheets("Data")
.Range("$A$1:$AI$80000").AutoFilter Field:=1, Criteria1:= _
"Actual"
.Range("$A$1:$AI$80000").AutoFilter Field:=2, Criteria1:="2018"
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.AutoFilter.Range.Offset(2).SpecialCells(xlCellTypeVisible).Cells(1, 35).Select 'SELECTS THE FIRST cell in A after deleting
ActiveCell.FormulaR1C1 = "=SUM(RC[-12]:RC[-1])"
.AutoFilter.Range.Offset(2).SpecialCells(xlCellTypeVisible).Cells(1, 35).Select
Selection.FillDown
End With
End Sub
How about something like below, instead of .FillDown, specify the range for the last column of visible data, and offset to the next column to enter the formula in there:
Sub Cal()
Dim LastRow As Long
With Worksheets("Data")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.Range("$A$1:$AI$" & LastRow).AutoFilter Field:=1, Criteria1:="Actual"
.Range("$A$1:$AI$" & LastRow).AutoFilter Field:=2, Criteria1:="2018"
'filter according to values specified
Set fltrdrng = .Range("$AI$2:$AI$" & LastRow).SpecialCells(xlCellTypeVisible)
'set the range of visible data on last column with data on your data-set
fltrdrng.Offset(0, 1).FormulaR1C1 = "=SUM(RC[-12]:RC[-1])"
'add the formula to the adjacent column by offsetting
End With
End Sub
This should do what you want. Autofilling is very dangerous with filtered data. This gets the activecell address and then creates the last cell address(row 80000 from your code), and then adds the formula to every cell in the range that is visible.
Start = ActiveCell.address
arow = ActiveCell.Row
alen = Len(arow)
lcell = Left(Start, Len(Start) - alen) & "80000"
Range(Start & ":" & lcell).SpecialCells(xlCellTypeVisible).Formula = "=SUM(RC[-12]:RC[-1])"
Related
how to copy a group of rows if cells in the first column equal a certain value.
this is the code i found online, but can't seem to get it to work, i think its because I've formated data on the sheet as a table.
Private Sub CommandButton1_Click()
a = Worksheets("inbd").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To a
If Worksheets("inbd").Cells(i, 3).Value = 76 Then
Worksheets("inbd").Rows(i).Copy
Worksheets("sheet2").Activate
b = Worksheets("sheet2").Cells(Rows.Count, 1).End(xlUp).Row
Worksheets("sheet2").Cells(b + 1, 1).Select
ActiveSheet.Paste
Worksheets("sheet1").Activate
End If
Next
Application.CutCopyMode = False
ThisWorkbook.Worksheets("inbd").Cells(1, 1).Select
End Sub
Example of how table on "inbd" looks:
How about the following, this will filter column A with the relevant criteria and copy the filtered rows into Sheet2, you will need to amend the range as I've used Column A to N, please also bear in mind in the code below I'm filtering Column A to find the value 76, whereas on your original code you were filtering Column C as your code Cells(i, 3).Value = 76 where 3 is the Column number:
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("inbd")
Dim wsDestination As Worksheet: Set wsDestination = Sheets("Sheet2")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'get the last row with data on Column A
ws.Range("A1:N" & LastRow).AutoFilter Field:=1, Criteria1:="76"
'filter data on Column 1 (A), change the Field number from 1 to the column number you wish to filter
ws.Range("A2:N" & LastRow).SpecialCells(xlCellTypeVisible).Copy
'copy filtered results
DestinationRow = wsDestination.Cells(wsDestination.Rows.Count, "A").End(xlUp).Row + 1
'get the destination row
wsDestination.Range("A" & DestinationRow).PasteSpecial xlPasteAll
'paste into Sheet2
Application.CutCopyMode = False
'deselect the copied rows
ws.Range("A1:N" & LastRow).AutoFilter Field:=1
'remove filter
End Sub
I'm trying to autofill this formula from AD2 down to the end of the dataset. But, instead, my macro will use the formula on AD1 (the column title) and not fill down. I've done this several times, but I can't figure out why it's acting up now. The obnoxious formula is reading the from the cell a few columns over (AB) and then declares one of three strings.
Dim lastRow As Long
lastRow = Cells(Rows.Count).End(xlUp).Row
Range("AD2").Select
Selection.FormulaR1C1 = _
"=IF(NOT(ISERROR(FIND(""iMac"",RC[-2]))),""iMac"",IF(NOT(ISERROR(FIND(""MacBook"",R[-21]C[-2]))),""MacBook"",""N/A""))"
Range("AD2").Select
Selection.AutoFill Destination:=Range("AD2:AD" & lastRow)
try to modify your var lastRow with ActiveSheet.Cells(ActiveSheet.Rows.Count, "AB").End(xlUp).Row
Sub test()
Dim lastRow As Long
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "AB").End(xlUp).Row
Range("AD2").Select
Selection.FormulaR1C1 = _
"=IF(NOT(ISERROR(FIND(""iMac"",RC[-2]))),""iMac"",IF(NOT(ISERROR(FIND(""MacBook"",R[-21]C[-2]))),""MacBook"",""N/A""))"
Range("AD2").Select
Selection.AutoFill Destination:=Range("AD2:AD" & lastRow)
End Sub
Try this. You are missing a column in your Cells (I have used column A so change to suit) and you don't need to select anything. In fact you probably don't need Autofill at all, just apply to the whole range in one go.
Sub y()
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
With Range("AD2")
.FormulaR1C1 = _
"=IF(NOT(ISERROR(FIND(""iMac"",RC[-2]))),""iMac"",IF(NOT(ISERROR(FIND(""MacBook"",R[-21]C[-2]))),""MacBook"",""N/A""))"
.AutoFill Destination:=Range("AD2:AD" & lastRow)
End With
End Sub
I have a spreadsheet with a column of values that I would like to divide by a fixed cell (say C3), and have the results in an adjacent column.
I would like this code to run to the last available row (with values) as well.
Would greatly appreciate any help! Thanks!
If your source values were in, for instance, A1:A7 and you want to copy them to B1:B7 and divide by C3 at the same time, you could:
With ActiveSheet
'Determine last row
Dim lastRow As Long
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
'Copy the original values from column A to column B
.Range("B1:B" & lastRow).Value = .Range("A1:A" & lastRow).Value
'Copy / Pastespecial Divide using cell C3
.Range("C3").Copy
.Range("B1:B" & lastRow).PasteSpecial Paste:=xlPasteValues, _
Operation:=xlDivide, _
SkipBlanks:=False, _
Transpose:=False
Application.CutCopyMode = False
End With
You can use Do While
Do While Cells(iCol, 3).Value <> ""
'Do some thing
iCol = iCol + 1
Loop
To sum up, I try to copy some filtered data from a workbook A to a workbook B keeping the formatting of the workbook B.
Here is the relevant part of my code:
With originSheet
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
.SpecialCells(xlCellTypeVisible).Copy
End With
End With
destinationSheet.Range("B4").PasteSpecial xlPasteValues
The paste special is not working and this is the formatting of the workbook A that is used.
Solved:
The problem was that you can't use PasteSpecial in a discontinuous range.
So I went with the solution of Siddharth Rout to go through all the areas of the filtered range:
With originSheet
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
Set filteredRange = .Offset(1, 0).SpecialCells(xlCellTypeVisible)
'~~> Loop through each area
For Each area In filteredRange.Areas
With destinationSheet
'~~> Find Next available row
lRow = .Range("B" & .Rows.Count).End(xlUp).Row + 1
area.Copy
destinationSheet.Range("B" & lRow).PasteSpecial xlPasteValues
End With
Next area
End With
End With
What #Jeeped has mentioned is very true that you cannot used Paste Special on a filtered range if they are Non Contiguous. However there is a way to achieve what you want :)
You have to loop through each area of the filtered range and then use Paste Special as shown below
Sub Sample()
Dim ws As Worksheet
Dim lastRowOriginSheet As Long
Dim filteredRange As Range, a As Range
Dim projectNumber As Long
'~~> I have set these for testing. Change as applicable
projectNumber = 1
Set ws = Sheet1
Set destinationSheet = Sheet2
lastRowOriginSheet = 16
With ws
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
Set filteredRange = .Offset(1, 0).SpecialCells(xlCellTypeVisible)
'~~> Loop through each area
For Each a In filteredRange.Areas
With destinationSheet
'~~> Find Next available row
lRow = .Range("B" & .Rows.Count).End(xlUp).Row + 1
a.Copy
destinationSheet.Range("B" & lRow).PasteSpecial xlPasteValues
End With
Next a
End With
End With
End Sub
In Action
PasteSpecial does not work on a discontiguous range. If you have one hidden row in among visible rows then you have a discontiguous range. However, due to the nature of a discontiguous range, a straight copy and paste will paste formats and the values from formulas; i.e. it cannot determine how to shift the cell ranges in formulas so it just pastes values.
With originSheet
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
'you should probably check to ensure you have visible cells before trying to copy them
.SpecialCells(xlCellTypeVisible).Copy destination:=destinationSheet.Range("B4")
End With
End With
Try this. Instead of doing PasteSpecial, since you just need values, you can set the ranges equal to eachother.
Dim copyRng As Range
With originSheet
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
Set copyRng = .SpecialCells(xlCellTypeVisible)
End With
End With
' destinationSheet.Range("B4").Value = copyRng.Value
With destinationSheet
.Range(.Cells(4, 2), .Cells(4 + copyRng.Rows.Count - 1, 2 + copyRng.Columns.Count - 1)).Value = copyRng.Value
End With
(this is assuming your worksheet and lastRow and projectNumber are all declared properly and working).
Edited because if you just do Range("B4").Value = Range("A1:Z100").Value, it's only going to put the first value in your copied range in the cell. You need to expand the destination range to be the size of the copy range.
I have a small programme which I want to loop through several worksheets. But the problem is in the code I have, there are variables that needs to be changed from worksheet to worksheet. Therefore I can't use the loop command.
In my code (please see below) I have set them as VARIABLE1, VARIABLE2 etc. The values of these should be changed when it run first time, second time and so on.
Example:
In the First Loop VARIABLE1 should be equal to "CMGLT" and in the
Second Loop VARIABLE1 should be equal to "CMCLT".
Function WorksheetExists(WSName As String) As Boolean
On Error Resume Next
WorksheetExists = Worksheets(WSName).Name = WSName
On Error GoTo 0
End Function
Sub BOI()
If Not WorksheetExists("VARIABLE1") Then '---------------VARIABLE1
Sheets.Add.Name = "VARIABLE1" '---------------VARIABLE1
Else
'START GEN CODE
'Set CMGLT as activesheet!!!!
Worksheets("VARIABLE1").Activate '---------------VARIABLE1
'Checking company code
Dim celltxt As String
celltxt = ActiveSheet.Range("G8").Text
If InStr(1, celltxt, "VARIABLE2") Then '---------------VARIABLE2
'unmerge entire sheet
ActiveSheet.Cells.UnMerge
'unwrap entire sheet
ActiveSheet.Cells.WrapText = False
'set short date format for up to 3000 rows
ActiveSheet.Range("A2", "A3000").NumberFormat = "dd/mm/yyyy"
'delete blank rows in column A
Range("A:A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
'delete rows from 1 to 6
Rows("1:6").EntireRow.Delete
'deleting all rows below "total"
Dim LR As Long, Found As Range
LR = Range("A" & Rows.Count).End(xlUp).Row
Set Found = Columns("A").Find(What:="Total", LookIn:=xlValues, lookat:=xlWhole)
If Not Found Is Nothing Then Rows(Found.Row & ":" & LR).Delete
'changing column width of B column
Range("B1").ColumnWidth = 12
'changing column width of A column
Range("A1").ColumnWidth = 12
'changing formating of B column to General
Range("B:B").NumberFormat = "General"
'CHANGE THIS AS APPROPRIATELY!!!!
Range("B1").Value = "VARIABLE3" '------------------------------------'VARIABLE3
'getting date as value
Range("C1").Select
ActiveCell.FormulaR1C1 = "=TEXT(RC[-2],""DD.MM.YYYY"")"
'copying company code and date until last row of data
Dim LRow As Long
LRow = ActiveSheet.UsedRange.Rows.Count
Range("B1").AutoFill Destination:=Range("B1:B" & LRow)
Range("C1").AutoFill Destination:=Range("C1:C" & LRow)
'pasting date as value
Columns("C:C").Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
'deleting blank rows in amount column
On Error Resume Next
Range("W:W").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
'coping data to "UP" sheet
Dim Lastrow As Integer
Lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
Range("C1:C" & Lastrow).Copy Destination:=Worksheets("Up").Range("A" & Rows.Count).End(xlUp).Offset(1)
Range("B1:B" & Lastrow).Copy Destination:=Worksheets("Up").Range("C" & Rows.Count).End(xlUp).Offset(1)
Range("C1:C" & Lastrow).Copy Destination:=Worksheets("Up").Range("D" & Rows.Count).End(xlUp).Offset(1)
Range("Q1:Q" & Lastrow).Copy Destination:=Worksheets("Up").Range("F" & Rows.Count).End(xlUp).Offset(1)
Range("Q1:Q" & Lastrow).Copy Destination:=Worksheets("Up").Range("I" & Rows.Count).End(xlUp).Offset(1)
Range("W1:W" & Lastrow).Copy Destination:=Worksheets("Up").Range("O" & Rows.Count).End(xlUp).Offset(1)
'END GEN CODE
Else
MsgBox ("VARIABLE1 Validation Mismatch. Exiting...") '---------------VARIABLE1
Exit Sub
End If
End If
End Sub
edited for some code speed improvements after OP's sharing an example file
you can have your BOI sub accept varying strings as parameters and be called by a main sub looping through all of them
like follows
Option Explicit
Sub main() '<~~ main sub calling BOI inside a loop
Dim VARIABLE1 As Variant, VARIABLE2 As Variant, VARIABLE3 As Variant
Dim i As Long
VARIABLE1 = Array("CMGLT", "CMCLT", "VARIABLE3", "VARIABLE4") '<~~ "main" array containing all VARIABLE1 needed values
VARIABLE2 = Array("114486744", "104074162", "VARIABLE2-3", "VARIABLE2-4") ' <~~ VARIABLE2 array, with elements corresponding by position to VARIABLE1 ones -> total elements number must match VARIABLE1 one
VARIABLE3 = Array("VARIABLE3-1", "VARIABLE3-2", "VARIABLE3-3", "VARIABLE3-4") ' <~~ VARIABLE3 array, with elements corresponding by position to VARIABLE1 ones -> total elements number must match VARIABLE1 one
For i = 0 To UBound(VARIABLE1) ' <~~ loop over your VARIABLE1 array
Call BOI(VARIABLE1(i), VARIABLE2(i), VARIABLE3(i)) ' <~~ and pass VARIABLE2 and VARIABLE3 corresponding elements, too
Next i
End Sub
Sub BOI(VARIABLE1 As Variant, VARIABLE2 As Variant, VARIABLE3 As Variant)
Dim LR As Long
Dim found As Range
If Not WorksheetExists(CStr(VARIABLE1)) Then '---------------VARIABLE1
Sheets.Add.Name = CStr(VARIABLE1)
Else
'START GEN CODE
With Worksheets(CStr(VARIABLE1)) '---------------VARIABLE1 '<~~ instead of selecting/activating wanted sheet, tell VBA to consider it as implicit object for any subsequent methods or properties calls
'Checking company code
If InStr(1, .Range("G8"), CStr(VARIABLE2)) Then '---------------VARIABLE2
'unmerge entire sheet
.UsedRange.UnMerge '<~~ VBA reads this statement as "Worksheets(CStr(VARIABLE1)).UsedRange.Unmerge"
'unwrap entire sheet
.UsedRange.WrapText = False '<~~ act on usedrange only, to be faster
LR = .Range("A" & .Rows.Count).End(xlUp).Row '<~~ store last non empty row index
'clearing all rows below "Total"
Set found = .Range("A2:A" & LR).SpecialCells(xlCellTypeConstants, xlTextValues).Find(what:="Total", LookIn:=xlValues, lookat:=xlWhole) '<~~ search into relevant cells only
If Not found Is Nothing Then .Rows(found.Row & ":" & LR).Clear '<~~ Clear() is faster then Delete()
'set short date format for up to 3000 rows
LR = .Range("A" & .Rows.Count).End(xlUp).Row '<~~ update last non empty row index (you possibly deleted some rows before)
' .Range("A2", "A3000").NumberFormat = "dd/mm/yyyy"
.Range("A2:A" & LR).SpecialCells(xlCellTypeConstants, xlNumbers).NumberFormat = "dd/mm/yyyy" '<~~ act on relevant cells only
'delete blank rows in column A
.Range("A1:A" & LR).SpecialCells(xlCellTypeBlanks).EntireRow.Delete '<~~ avoid deleting blank rows after the last non empty one
'delete rows from 1 to 6
.Rows("1:6").EntireRow.Delete
LR = .Range("A" & .Rows.Count).End(xlUp).Row '<~~ update last non empty row index (you possibly deleted some rows before)
'changing column width of B column
.Range("B1").ColumnWidth = 12
'changing column width of A column
.Range("A1").ColumnWidth = 12
'changing formating of B column to General
.Range("B1:B" & LR).NumberFormat = "General" '<~~ act on relevant cells only
'CHANGE THIS AS APPROPRIATELY!!!!
.Range("B1").Value = VARIABLE3 '------------------------------------'VARIABLE3
'getting date as value
.Range("C1").FormulaR1C1 = "=TEXT(RC[-2],""DD.MM.YYYY"")" '<~~ instead of selecting and then acting on selection, just act directly on the range object
'copying company code and date until last row of data
.Range("B1").AutoFill Destination:=Range("B1:B" & LR)
.Range("C1").AutoFill Destination:=Range("C1:C" & LR)
'pasting date as value
' .Columns("C:C").Select
' Selection.Copy
' Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
' :=False, Transpose:=False
' Application.CutCopyMode = False
With .Columns("C:C").SpecialCells(xlCellTypeFormulas) '<~~ this is equivalent to what above, but much faster
.Value = .Value
End With
'deleting blank rows in amount column
On Error Resume Next
.Range("W1:W" & LR).SpecialCells(xlCellTypeBlanks).EntireRow.Delete '<~~ act on relevant cells only
On Error GoTo 0 '<~~ always remember to set standard error trapping right after you don't need skipping errors anymore
'coping data to "UP" sheet
LR = .Cells(Rows.Count, 1).End(xlUp).Row '<~~ update last non empty row index (you possibly deleted some rows before)
CopyValues .Range("C1:C" & LR), Worksheets("Up"), "A" '<~~ take advantage of a sub to avoid repeating same code
CopyValues .Range("B1:B" & LR), Worksheets("Up"), "C" '<~~ take advantage of a sub to avoid repeating same code
CopyValues .Range("C1:C" & LR), Worksheets("Up"), "D" '<~~ take advantage of a sub to avoid repeating same code
CopyValues .Range("Q1:Q" & LR), Worksheets("Up"), "F" '<~~ take advantage of a sub to avoid repeating same code
CopyValues .Range("Q1:Q" & LR), Worksheets("Up"), "I" '<~~ take advantage of a sub to avoid repeating same code
CopyValues .Range("W1:W" & LR), Worksheets("Up"), "O" '<~~ take advantage of a sub to avoid repeating same code
'END GEN CODE
Else
MsgBox (VARIABLE1 & " Validation Mismatch. Exiting...") '---------------VARIABLE1
Exit Sub
End If
End With
End If
End Sub
Sub CopyValues(sourceRng As Range, targetSht As Worksheet, targetCol As String)
With targetSht
.Range(targetCol & .Rows.Count).End(xlUp).Offset(1).Resize(sourceRng.Rows.Count).Value = sourceRng.Value
End With
End Sub
Function WorksheetExists(WSName As String) As Boolean
On Error Resume Next
WorksheetExists = Worksheets(WSName).Name = WSName
On Error GoTo 0
End Function
where I also made some more (of all possible) little code optimizations too