Excel VBA Fill Series - excel

I need to fill a range from 10,000 all the way until the end of the column. I have the lRow variable that is finding the last row, and then I have the IF loop that is filling the range just like I need to. The clear contents part is to remove any previous values and fill new ones. The Range("E2").Value = 1 is there to start the series.
The problem is I can't get it to fill the Range("F2:F" & lRow) in a step value of 10,000. The macro just fills out in a step value of 1. Any ideas?
I've tried recording macros but it never works quite right. It needs to be sort of dynamic as the list will grow over time.
This is what it should look like:
Sub MLOS_PriorityTable_StepValues()
Dim ws As Worksheet
Dim lRow As Long
Dim featOrder As Range
Dim Style As Range
Dim dataRange As Range
Dim currentArea As Range
Set ws = ActiveSheet
Set featOrder = ws.Range("A1:ZZ1").Find("FeatureOrder")
Set Style = ws.Range("A1:ZZ1").Find("Style")
Range("E2:E1000").ClearContents
Range("F2:F1000").ClearContents
Range("E2").Value = 1
Range("F2").Value = 10000
lRow = Cells(Rows.Count, Style.Column).End(xlUp).Row
On Error Resume Next
Set dataRange = Range("E2:E" & lRow).SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If Not dataRange Is Nothing Then
For Each currentArea In dataRange.Areas
With currentArea
With .Offset(-1, 0).Resize(.Rows.Count + 1)
.Cells(1).AutoFill Destination:=.Cells, Type:=xlFillSeries
End With
End With
Next currentArea
End If
On Error Resume Next
Set dataRange = Range("F2:F" & lRow).SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If Not dataRange Is Nothing Then
For Each currentArea In dataRange.Areas
With currentArea
With .Offset(-1, 0).Resize(.Rows.Count + 1)
.Cells(1).AutoFill Destination:=.Cells, Type:=xlFillSeries
End With
End With
Next currentArea
End If
End Sub

Is the result of F just E*10000? It doesn't matter too much, there's a few ways to solve.
Find your last row number (which you did as lRow)
Iterate for i = 2 to lRow which will loop from the top row with data to the last
Do something in here like Range("F"&i) = Range("E"&i)*10000 assuming that is the relationship or Range("F"&i) = Range("F"&i-1)+10000
next i
it will just iterate cell by cell, make the calculation, and move on.
You may need to make it more robust if you have a script that hops around multiple sheets or workbooks so the range or cell or whatever references you use are correct.
Don't know how fast this would be vs the fill Series function but it doesn't look at face value like it would be too slow.
let me know how you get on!
Rob S

Related

Using IsNumeric to delete rows of data

I have two columns of data I am cleaning up using VBA. If the value in column A is non-numeric or blank, I need to delete the entire row. Below is a sample of the data and the code I am trying to use. It seems to be completely skipping over the portion of the code that deletes the rows if IsNumeric returns false.
9669 DONE
9670 OPEN
Order # STATUS
9552
9672
Code that isn't working.
Dim cell As Range
For Each cell In Range("A1:A" & max_col)
If IsNumeric(cell) = False Then
Cells(cell, 1).Select
Rows(cell).EntireRow.Delete
Exit For
End If
Next cell
Any help is appreciated!
use just
With Range("A1", Cells(Rows.Count, 1).End(xlUp))
.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
.SpecialCells(xlCellTypeConstants, xlTextValues).EntireRow.Delete
End With
or, if you don't know for sure whether there will be empty or not numeric cells
With Range("A1", Cells(Rows.Count, 1).End(xlUp))
If WorksheetFunction.CountBlank(.Cells) > 0 Then .SpecialCells(xlCellTypeBlanks).EntireRow.Delete
If WorksheetFunction.Count(.Cells) < .Rows.Count Then .SpecialCells(xlCellTypeConstants, xlTextValues).EntireRow.Delete
End With
Loop from the bottom
Dim max_col as long
max_col = 100
Dim i as Long
For i = max_col to 1 step -1
If Not isnumeric(activesheet.cells(i,1)) then
activesheet.rows(i).delete
End If
Next i
When deleting (or adding, for that matter) rows you need to loop backwards through the data set - See #ScottCraner's example for an exact answer like that - or, you create the range of cells to delete then delete at once, like below:
Dim rowNo as Long
For rowNo = 1 to max_col
Dim cell as Range
Set cell = Range(rowNo,1)
If IsNumeric(cell) Then
Dim collectRows as Range
If collectRows is Nothing Then
Set collectRows = cell
Else
Set collectRows = Union(collectRows,cell)
End If
End If
Next
collectRows.EntireRow.Delete

Finds cells on multiple conditions and cut paste the same to another sheet (Simplified)

Any alternative or suggestions to Fasten the below stated code that finds cells on multiple conditions and cut paste the same to another sheet.
Sub test()
'For Move Entire Row to New Worksheet if Cell Contains Specific Text's
'Using autofilter to Copy rows that contain certain text to a sheet called commodity
Dim LR As Long
Range("A2").EntireRow.Insert Shift:=xlDown
LR = Sheets("Data").Cells(Rows.Count, "E").End(xlUp).Row
LR1 = Sheets("Commodity").Cells(Rows.Count, "A").End(xlUp).Row + 1
With Sheets("Data").Range("e:e")
.AutoFilter Field:=1, Criteria1:=("*SILVER*")
.SpecialCells(xlCellTypeVisible).EntireRow.Copy
Destination:=Sheets("Commodity").Range("A" & LR1)
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
With Sheets("Data").Range("e:e")
.AutoFilter Field:=1, Criteria1:=("*GOLD*")
.SpecialCells(xlCellTypeVisible).EntireRow.Copy
Destination:=Sheets("Commodity").Range("A" & LR1)
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
With Sheets("Data").Range("e:e")
.AutoFilter Field:=1, Criteria1:=("*MCX*")
.SpecialCells(xlCellTypeVisible).EntireRow.Copy
Destination:=Sheets("Commodity").Range("A" & LR1)
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
End Sub
As well as the good suggestions #ShaiRado is making, what's slowing this down is your repeated interactions with the Excel functions and interface. Ideally, you'd read the data into variables within VBA, and then, all within VBA, check for matches and prepare an output array. That way you'd only have one interaction between VBA and Excel, namely to write the output array to your target sheet. It's also costly in time to delete one row at a time, so you might be better to create just one 'delete range' and hit it all in one go.
Skeleton code to achieve this is given below (but note, it will need a 'column offset' calculation if your used range doesn't start at "A", and you might choose a more reliable function than UsedRange). You call the routine like so:
TransferData "Silver", "Gold", "MCX"
And the routine itself might like something like this:
Private Sub TransferData(ParamArray searchItems() As Variant)
Dim srcData As Variant
Dim txData() As Variant
Dim item As Variant
Dim r As Long, c As Long
Dim txIndexes As Collection
Dim delRng As Range
'Read source data into an array
'Note: I've used UsedRange as I don't know your sheet layout
srcData = ThisWorkbook.Worksheets("Data").UsedRange.Value2
'Check for matches and record index number
Set txIndexes = New Collection
For r = 1 To UBound(srcData, 1)
For Each item In searchItems
If srcData(r, 5) = item Then
txIndexes.Add r
Exit For
End If
Next
Next
'Trasfer data to output array
ReDim txData(1 To txIndexes.Count, 1 To UBound(srcData, 2))
r = 1
For Each item In txIndexes
For c = 1 To UBound(srcData, 2)
txData(r, c) = srcData(item, c)
Next
r = r + 1
Next
'Write the transfer data to target sheet
With ThisWorkbook.Worksheets("Commodity")
.Cells(.Rows.Count, "A").End(xlUp).Resize(UBound(txData, 1), UBound(txData, 2)) = txData
End With
'Delete the transfered rows
For Each item In txIndexes
With ThisWorkbook.Worksheets("Data")
If delRng Is Nothing Then
Set delRng = .Cells(item, "A")
Else
Set delRng = Union(delRng, .Cells(item, "A"))
End If
End With
Next
If Not delRng Is Nothing Then delRng.EntireRow.Delete
End Sub

VBA to add totals and formula to multiple sheets

I have an excel sheet with around 200 work sheets each containing a list of products sold to a company.
I need to add
A total at the bottom of row D-G where the bottom can be a different value. I.E. E4
below the total a formula based on the total. I.E. if E4 (being the bottom of the above row) is below $999 the display text "samples", if between 1000-3000 then multiply E4 by 2%, 3001-7500 x 5% etc.
I need to be able to add it to the entire workbook easily using vba. Since I must do this to numerous ss it would literally save me 15-20 hours a month.
Edit:
So I have something that seems to be the right path.
Sub Split_Worksheets()
Dim rRange As Range, rCell As Range
Dim wSheet As Worksheet
Dim wSheetStart As Worksheet
Dim strText As String
Set wSheetStart = ActiveSheet
wSheetStart.AutoFilterMode = False
'Set a range variable to the correct item column
Set rRange = Range("A1", Range("A65536").End(xlUp))
'Delete any sheet called "UniqueList"
'Turn off run time errors & delete alert
On Error Resume Next
Application.DisplayAlerts = False
Worksheets("UniqueList").Delete
'Add a sheet called "UniqueList"
Worksheets.Add().Name = "UniqueList"
'Filter the Set range so only a unique list is created
With Worksheets("UniqueList")
rRange.AdvancedFilter xlFilterCopy, , _
Worksheets("UniqueList").Range("A1"), True
'Set a range variable to the unique list, less the heading.
Set rRange = .Range("A3", .Range("A65536").End(x2Up))
End With
On Error Resume Next
With wSheetStart
For Each rCell In rRange
strText = rCell
.Range("A1").AutoFilter 1, strText
Worksheets(strText).Delete
'Add a sheet named as content of rCell
Worksheets.Add().Name = strText
'Copy the visible filtered range _
(default of Copy Method) and leave hidden rows
.UsedRange.Copy Destination:=ActiveSheet.Range("A1")
ActiveSheet.Cells.Columns.AutoFit
Next rCell
End With
With wSheetStart
.AutoFilterMode = False
.Activate
End With
On Error GoTo 0
Application.DisplayAlerts = True
Dim colm As Long, StartRow As Long
Dim EndCell As Range
Dim ws As Worksheet
StartRow = 3
For Each ws In Worksheets
Set EndCell = ws.Cells(Rows.Count, "c").End(xlUp).Offset(1, 1)
If EndCell.Row > StartRow Then EndCell.Resize(, 4).Formula = "=SUM(R" & StartRow & "C:R[-1]C)"
Set EndCell = ws.Cells(Rows.Count, "D").End(xlUp)
If EndCell.Row >= 1000 Then
Range(J2) = Formula = ((EndCell.Row) * (0.05))
Range(J3) = "5% Discount"
ElseIf EndCell.Row >= 3000 Then
Range(J2) = Formula = ((EndCell.Row) * (0.1))
Range(J3) = "10% Discount"
End If
Next ws
End Sub'
Just need to figure out how to display the results and text to the right cells (J2 in this case)
I will supply the logic and all the references you need to put this one together; and will let you try to put it together on your own :). Come back for more help if needed.
You need to loop through all the worksheets in your workbook (Microsoft Tutorial)
You need to find the last row for the given columns (Online tutorial)
You need to use an IF statement to choose which formula to use (MSDN reference)
UPDATE
What's wrong with your code is this line :
Range(J2) = Formula = ((EndCell.Row) * (0.1))
What you're telling the computer is :
Multiply EndCell.Row by 0.1 (which has the number of the row below and to the right of the last cell in column C)
Compare Formula with the result previously obtained
Store the result of that logical expression at the range stored in variable J2
First of all, what you want is to put the result of the equation, and want to change J2 to "J2" so it gets the cell J2, instead of the what's contained in J2 (which has nothing at that point)
Also, you seem to say that you're not getting the right cells, maybe it is caused by this :
Set EndCell = ws.Cells(Rows.Count, "c").End(xlUp).Offset(1, 1)
In that line, you're finding the last cell of column C, but then you select the cell below, and to the right of it.
There are so many things wrong with your code it's hard to say what's not working properly.

VBA range selection / clearing error

I'm trying to clear a certain row on the columns A:H and K in my sheet using vba.
In column K there's not only a value, also a checkbox.
I'd like to leave column I en J as they are since there's a formula in those rows.
Now I've tried a lot of different options found shattered on the internet, but can't seem to fix the problem.
My code is as following:
Sub ClearSelected()
Sheets("overview").Unprotect
Sheets("Database").Unprotect
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim rng As Range
Dim counter As Integer
Dim vert As Integer
Dim r As Range
Dim chkbx As CheckBox
Set ws1 = Worksheets("Overview")
Set ws2 = Worksheets("Database")
Set rng = ws1.Range("P2")
vert = rng.Value + 1
counter = 2
'do Loop
Worksheets("Database").Activate
Do While counter < vert
'if "True", remove row
If ws2.Range(ws2.Range("K" & counter)) = True Then
ws2.Range("A" & counter & ":H" & counter).Select
Selection.Clear
ws2.Range("K" & counter).Select
Selection.Clear
'Remove checkbox in selectie
Set r = Selection
For Each chkbx In ActiveSheet.CheckBoxes
If Not Intersect(r, chkbx.TopLeftCell) Is Nothing Then chkbx.Delete
Next chkbx
rng.Value = rng.Value - 1
'remove checkbox
End If
counter = counter + 1
Loop
Sheets("overview").Protect AllowUsingPivotGraphs:=True
Sheets("Database").Protect
End Sub
For some reason it's failing on the range selection/clearing.
I'm getting the errormessage 1004.
Hope you have a good suggestion for me.
I'm not entirely sure if this will work, but have you tried changing the select.clear lines to something like
ws2.Range("K" & counter).ClearContents
This clears the cells without affecting the formatting, which might possibly causing the issue. Sometimes 1004 is also down to a loss of focus on an object, or the size of the range you are trying to operate on.
BTW, you may also not need to worry too much about deleting formulas. You can re-insert them automatically.
Something like:
ws2.Range("K" & counter).FormulaR1C1="=(R+1*C+1)"
'R and C refer to row and column, so equations remain relatively referenced
The easiest way is to start recording a macro, input the desired formula into a cell, and then view the code to find out how to structure the formula in VBA.
Not sure if any of this will help, but here's hoping.

Excel VBA or not to VBA, replace text if different between two cells

I have a quandary, and I don't know if it will work better using excel VBA or not. Thinking about it I believe VBA will work best, but I don't know how to make it work.
I have two pages in a workbook, one is the form, the other is the database, I want the pulldown menu from the form to populate the rest of the form. It does... what I want then is to be able to change the value of the form press submit, and the new data will overwrite the old data.
Is this possible?
Here is the link to the sheet I'm talking about.
http://dl.dropbox.com/u/3327208/Excel/Change.xlsx
Here is the script I am working with now...it takes the sheet, copies everything to a row takes that row, moves it to the NCMR Data tab and then clears the data on the new row from the original sheet.
This code technically could work, but what I need to do is make it use the same concept, but instead of creating a new row at the end of the sheet find the original line and replace the data from B to U in whatever row it was originally in.
I know it's possible, I just don't know how.
'Copy Ranges Variable
Dim c As Variant
'Paste Ranges Variable
Dim p As Range
'Setting Sheet
Set wsInt = Sheets("Form")
Set wsNDA = Sheets("Data")
Set p = wsInt.Range("A14")
With wsInt
c = Array(.Range("B11"))
End With
For i = LBound(c) To UBound(c)
p(i + 1).Value = c(i).Value
Next
With wsNDA
Dim Lastrow As Long
Lastrow = .Range("B" & Rows.Count).End(xlUp).Row + 1
wsInt.Rows("14").Copy
With .Rows(Lastrow)
.PasteSpecial Paste:=xlPasteFormats
.PasteSpecial Paste:=xlPasteValues
.Interior.Pattern = xlNone
End With
With .Range("A" & Lastrow)
If Lastrow = 3 Then
.Value = 1
Else
.Value = Val(wsNDA.Range("A" & Lastrow - 1).Value) + 1
End If
.NumberFormat = "0#######"
End With
End With
End Sub
I found this code:
Sub CopyTest()
Dim selrow As Range, rngToCopy As Range
With Worksheets("PD DB")
Set selrow = .Range("B:B").Find(.Range("BA1").Value)
'find the cell containing the value
Set rngToCopy = Union(selrow.Offset(0, 9), selrow.Offset(0, 12))
'use offset to define the ranges to be copied
rngToCopy.Copy Destination:=Worksheets("Edit Sheet").Range("B50")
'copy and paste (without Select)
End With
End Sub
As far as I can tell this will do what I want mostly, but I can't seem to figure out where to break it up to add it where I need to to make it work the way I want it to.
What I can tell is this, it will copy and paste, but I want to make sure it will paste the data into row it finds, and not overwrite the number of said row.
Can someone help make that possible with the two scripts I have here?
Not tested, but should get you started. I added a 3rd sheet (shtMap) to hold the mmapping between the cell addresses on your form and the column numbers on the "Data" sheet. Useful to name your sheets directly in the VB editor: select the sheet and set the name in the property grid.
*EDIT:*If you want to trigger the transfer on selecting a record id from a list in Range AG3 then place this code in the code module for that worksheet:
Private Sub Worksheet_Change(ByVal Target As Range)
Static bProcessing As Boolean
Dim rng As Range
If bProcessing Then Exit Sub
Set rng = Target.Cells(1)
If Not Application.Intersect(rng, Me.Range("AG3")) Is Nothing Then
bProcessing = True
'this is where you call your macro to transfer the record
bProcessing = False
End If
End Sub
You could use something like this for the transfer:
Public Enum XferDirection
ToForm = 1
ToDataSheet = 2
End Enum
Sub FetchRecord()
TransferData XferDirection.ToForm
End Sub
Sub SaveRecord()
TransferData XferDirection.ToDataSheet
End Sub
Sub TransferData(Direction As XferDirection)
Dim rngMap As Range, rw As Range, f As Range, dataCell As Range
Dim formCell As Range, dataCol As Long, dataRow As Long
Dim sId As String
sId = shtForm.Range("AG3").Value
Set f = shtData.Columns(1).Find(sId, LookIn:=xlValues, lookat:=xlWhole)
If Not f Is Nothing Then
dataRow = f.Row
Else
'what do you want to do here?
' record doesn't exist on data sheet
MsgBox "Record '" & sId & "' not found on '" & shtForm.Name & "' !"
Exit Sub
End If
Set rngMap = shtMap.Range("A2:B10")
For Each rw In rngMap.Rows
'the cell on the edit form
Set formCell = shtForm.Range(rw.Cells(1).Value)
'column # on datasheet
Set dataCell = shtData.Cells(dataRow, rw.Cells(2).Value)
If Direction = XferDirection.ToDataSheet Then
dataCell.Value = formCell.Value
Else
formCell.Value = dataCell.Value
End If
Next rw
End Sub
Matt, there are two approaches I would take. The first is use find(), which returns a range object, then append ".row" so that you'll be able to modify the row on Sheet2 (wsNDA, I think). You may want to test that find() doesn't return Nothing.
Dim foundRow as Long
Dim foundRng as Range
set foundRng = wsNDA.find(wsInt.Range("B11").Value, ...)
If Not foundRng is Nothing Then
foundRow = foundRng.row
End If
'method without check: foundRow = wsNDA.find(wsInt.Range("B11").Value, ...).Row
The other is to use a Dictionary object. I'm not sure what you'd want for the key, but the item could be the row on the data sheet. When you make the change to what's on the form, check against the key and grab its item (the corresponding row) to determine where you need to replace the values.

Resources