Making a userform to add data to a database in VBA. What i'm wanting to do is pretty simple; to select the last filled row, and skip to the next empty cell in the row below.
Screenshot added with the problem highlighted.
Question is quite simple, what am I doing wrong, and how would it be fixed?
.End(... has nothing to reference to. If you wanted to add a linebreak, do so by writing:
Irow = ws.Cells(Rows.Count, 1) _
.End(xlUp).Offset(1,0).Row
Watch the _ with a space before it at the end of the first line.
This will connect the two lines, forming one valid statement.
Alternatively, just write the two lines as one:
Irow = ws.Cells(Rows.Count, 1).End(xlUp).Offset(1,0).Row
Related
I am trying to run a for loop from start to end of all of the data in my excel sheet that is being processed by access vba. I have tried:
myWorksheet.Cells(Rows.Count, 4).End(xlUp).row
but it did not work. I am confused how I can get a one number output so that it is able to run in my loop successfully. Please help. Also, if you could break down the format of your answer that would really help me. Thanks
Access has not constant xlUp which has value -4162 in Excel. In Access xlUp is non-declared variable with Variant type and initial value Empty. To use proper End(xlUp) in Access VBA you can write .End(-4162):
myWorksheet.Cells(myWorksheet.Rows.Count, 4).End(-4162).row
I usually define my LastRow like so, with "N" that's just the column that has the data in it, you can adjust that as needed:
LastRow = ActiveSheet.Cells(Cells.Rows.Count, "N").End(xlUp).Row
I usually pair that with a Dim row_no As Long and use it in the loop like so, the hard number here being the first row of my dataset.
For row_no = 5 to LastRow
'Do the thing
Next
There's way to iterate through loops in reverse order, which makes more sense when you're deleting rows, because when you delete it row it messes with what row the macro thinks it's on. Ie: you delete row 2, so row 3 moves to row 2, but the macro moves to row 3 which has the 4th row of data. For those you'll approach it like so:
For x = LastRow To 5 Step -1
'Do the thing
Next x
I have an excel where I have 2 sheets and I want to import selected columns to another sheet and delete possible duplicate rows. The problem is, that when I run my code, it deletes my latest duplicate row, eventhough I want to save latest row and delete the other 'older' duplicate. I really appreciate any help! Thank you :) Here is my code what I have tried so far:
Private Sub CommandButton1_Click()
Cells.RemoveDuplicates Columns:=Array(1, 2, 3)
Dim lastrow As Long, erow As Long
lastrow = Worksheets("one").Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lastrow
Worksheets("one").Cells(i, 1).Copy
erow = Worksheets("two").Cells(Rows.Count, 1).End(xlUp).Row
Worksheets("one").Paste Destination:=Worksheets("two").Cells(erow + 1, 1)
Worksheets("one").Cells(i, 3).Copy
Worksheets("one").Paste Destination:=Worksheets("two").Cells(erow + 1, 2)
Next i
End Sub
Another question is, I have tried this code for test excel, but where I want to use this macro has a lot larger data. It takes a very long time to go through all the data.. is the problem in my code or is the actual excel just so big? I hope I explain everything crearly.
Thank you!
"The problem is, that when I run my code, it deletes my latest duplicate row..."
The problem is the sort order of your data.
Remove Duplicates moves from first index to last and and keeps the first occurrence. So you either need to sort your data such that your "oldest" duplicate is the first occurrence at top or you have to use a different method to remove duplicates.
"is the problem in my code or is the actual excel just so big?"
It could be both, but one thing is certain, your code can definitely run faster.
The low hanging fruit is to put Application.EnableScreenUpdating = False in the beginning of you sub. That stops Excel from updating the screen when your runs. It will make a huge difference. Just remember to set it back to True again.
You're also looping through a range one row at a time but I don't see any particular reason why you need to. You can copy the while range in a single statement without looping. You don't even need to copy of you don't care about ordering the format, you could transfer the values just make the ranges equal each other.
An example of how you to transfer values without cutting or looping
Worksheets("two").Range("A2:A" & lastrow).Value = Worksheets("one").Range("E2:E" & lastrow).Value
You can use the same approach with copy and paste if you prefer.
I am currently trying to remove rows from column A to column V based on duplicates found in column A.
my current formula is:
Workbooks(TgtWB).ActiveSheet.Range("A15:V500").RemoveDuplicates _
Columns:=Array(1), Header:=xlYes
My header is located on row 15.
the error i am getting is
Application-defined or object-defined error
I have tried switching ActiveSheet with worksheet("xxx") but still doesn't seem to work either. i'm not sure what i'm doing wrong here.
Try (note worksheetS, not worksheet),
Workbooks(TgtWB).worksheetS("xxx").Range("A15:V500").RemoveDuplicates Columns:=1, Header:=xlYes
'or,
ActiveSheet.Range("A15:V500").RemoveDuplicates Columns:=1, Header:=xlYes
If TgtWB is open, it may or may not contain the ActiveSheet. Each open workbook does not have an ActiveSheet. There is only one ActiveSheet for the application instance. ActiveSheet is not a property of the Workbooks collection.
Using built in functionality is great, unless you're a beginner like myself and the .Applications and .Worksheets get overwhelming and confusing. For a small data set, such as columns A through V (depending obviously on how many rows you have), a loop and if-statement not only can work well, but can be good practice as well! Something like this may work well:
Dim CurrentValue, LastValue As Variant
LastValue = ""
For I = 1 To 500
CurrentValue = Sheets("Sheet 1").Range("A" & i).Value
If CurrentValue = LastValue Then
Sheets("Sheet 1").Range("A" & i & ":V" & i).Clear Contents
Else
LastValue = CurrentValue
End If
Next i
This will loop through every row, ask if the current value in the cell is the same as the one previously observed (except the first one, the first one will automatically be saved by nature), and if it has been, it will clear the contents of the row. If it's not the same, it will assign it to the new variable and begin looking for new ones.
There are two drawbacks with this method that can be solved by simply adapting the code to your needs:
Non-Sequential Items: If the values that are duplicates are not sequential, the code won't kick them out. At that point I recommend using an application code or a dictionary. Dictionaries are infinitely useful for storing unique data entries and skipping duplicates.
Deleting Rows: When you delete rows in a loop it messes up your loop, throwing off the order of your incrementer. The best way to combat this is by first clearing the columns then having your code loop for empty rows and delete them outside of your loop.
This is very basic but something I've found greatly helpful as a beginner and hopefully other beginners and yourself can learn something from it.
Happy Coding!
I would like to know if there is a faster way do this than the code I am using. I got the code using xlUp from the recorder.
rCnt = Cells(Rows.Count, "B").End(xlUp).Row
ActiveSheet.Range("$B$1:$J" & rCnt).AutoFilter Field:=5, _
Criteria1:=Application.Transpose(arrCodes), Operator:=xlFilterValues
Rows("2:" & rCnt).Delete Shift:=xlUp
And actually, if there was some way to flip the filter, I wouldn't need to delete at all as this is a temporary table that I copy from. However, all my research has failed to find a way to do
Criteria1:=Application.Transpose(<>arrCodes)
and arrCodes has too many elements to list in the filter. And the stuff that is not in arrCodes is way too numerous to make an array from. Thanks.
If you want to just use Excel UI and not formulas or VBA, you can do the following simple steps to get an "inverse" filter. This could then be ported to VBA if needed:
Apply the filter with the opposite conditions
Color those cells in one column (either font or background)
Clear the filter
Filter again but this time by cells in that column without color
Copy those results where you want them
This will not work well if the column already has some background colors. If that is the case, you can add a new column and color it. If this is in VBA, you could automate those steps. There are limits, but this is quick and simple if it applies.
I've had success in the past with building then deleting a range. You can combine ranges with Union(). I've attached a bit of example code, it's not wonderful but it shows the basic concept. This example deletes rows with odd numbers in column A in rows 2 through 11.
Public Sub DeleteRows()
Dim deleteThis As Range
For i = 2 To 11
If Sheet1.Cells(i, 1).Value Mod 2 = 1 Then
If deleteThis Is Nothing Then
Set deleteThis = Sheet1.Rows(i)
Else
Set deleteThis = Union(deleteThis, Sheet1.Rows(i))
End If
End If
Next i
deleteThis.Delete xlShiftUp
End Sub
I have this piece of code which finds the excel row of an item from a list and deletes the items from a list. What I want... is to delete the Excel row as well.
The code is here
Private Sub imperecheaza_Click()
Dim ws As Worksheet
Dim Rand As Long
Set ws = Worksheets("BD_IR")
Rand = 3
Do While ws.Cells(Rand, 4).Value <> "" And Rand < 65000
If ws.Cells(Rand, 4).Value = gksluri.Value * 1 And ws.Cells(Rand, 5).Value = gksluri.List(gksluri.ListIndex, 1) * 1 Then
ws.Range(Rand, 1).EntireRow.Delete '(here I want to delete the entire row that meets the criteria from the If statement)
gksluri.RemoveItem gksluri.ListIndex
Exit Do
End If
Rand = Rand + 1
Loop
End Sub
Where I added ws.Range(Rand,1).EntireRow.Delete is where I want to delete the entire row but I don't know how to do it. What I want... if it finds the same value in a cell like in some selected item of my list to be able to remove both the entire row in excel and the item from the listbox. It works to remove the item from the listbox but I don't know how to remove the row as well
Chris Nielsen's solution is simple and will work well. A slightly shorter option would be...
ws.Rows(Rand).Delete
...note there is no need to specify a Shift when deleting a row as, by definition, it's not possible to shift left
Incidentally, my preferred method for deleting rows is to use...
ws.Rows(Rand) = ""
...in the initial loop. I then use a Sort function to push these rows to the bottom of the data. The main reason for this is because deleting single rows can be a very slow procedure (if you are deleting >100). It also ensures nothing gets missed as per Robert Ilbrink's comment
You can learn the code for sorting by recording a macro and reducing the code as demonstrated in this expert Excel video. I have a suspicion that the neatest method (Range("A1:Z10").Sort Key1:=Range("A1"), Order1:=xlSortAscending/Descending, Header:=xlYes/No) can only be discovered on pre-2007 versions of Excel...but you can always reduce the 2007/2010 equivalent code
Couple more points...if your list is not already sorted by a column and you wish to retain the order, you can stick the row number 'Rand' in a spare column to the right of each row as you loop through. You would then sort by that comment and eliminate it
If your data rows contain formatting, you may wish to find the end of the new data range and delete the rows that you cleared earlier. That's to keep the file size down. Note that a single large delete at the end of the procedure will not impair your code's performance in the same way that deleting single rows does
Change your line
ws.Range(Rand, 1).EntireRow.Delete
to
ws.Cells(Rand, 1).EntireRow.Delete
Better yet, use union to grab all the rows you want to delete, then delete them all at once. The rows need not be continuous.
dim rng as range
dim rDel as range
for each rng in {the range you're searching}
if {Conditions to be met} = true then
if not rDel is nothing then
set rDel = union(rng,rDel)
else
set rDel = rng
end if
end if
next
rDel.entirerow.delete
That way you don't have to worry about sorting or things being at the bottom.
Something like this will do it:
Rows("12:12").Select
Selection.Delete
So in your code it would look like something like this:
Rows(CStr(rand) & ":" & CStr(rand)).Select
Selection.Delete