I have this code where i can search for the first two blank cells and place an "x" on the first cell that contains the blank. For testing purposes i have separated the the code into two command buttons. For First command button searches the blank cells and places the "x" and second command button finds the "x" and deletes the row and all other rows after it.
My problem is, i want it to delete all rows after the "x" but to leave the last row which contains the overall Total.
Here is my code from the two command buttons :
Sub findEmptyCells()
Dim lastRow As Long, i As Long
Dim firstEmptyCell As Range
lastRow = Cells(Rows.Count, 12).End(xlUp).Row
For i = 12 To lastRow
If Cells(i, 12).Value = "" And Cells(i + 1, 12).Value = "" Then
Set firstEmptyCell = Cells(i, 12)
Exit For
End If
Next i
MsgBox ("There are no two empty cells in a row")
Exit Sub
End If
firstEmptyCell.Value = "x"
End Sub
Sub Deleterows_Click()
Dim srchRng As Range
Set srchRng = Range("L7:L500")
Dim c As Range
For Each c In srchRng
If c.Value = "x" Then
Range(c, Range("L500")).EntireRow.Delete
Exit For
End If
Next
End Sub
Delete Rows Identified By Merged Cells
Option Explicit
Sub DeleteRows()
Const Col As String = "L"
Const fRow As Long = 13
Const mcCount As Long = 5
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, Col).End(xlUp).Row
Dim cCell As Range
Dim r As Long
For r = fRow To lRow - mcCount
'Debug.Print r
Set cCell = ws.Cells(r, Col)
If cCell.MergeArea.Cells.Count = mcCount Then
If Len(CStr(cCell.Value)) = 0 Then
cCell.Offset(-1).Resize(lRow - r + 1).EntireRow.Delete
Exit For
End If
r = r + mcCount - 1
End If
Next r
MsgBox "Rows deleted.", vbInformation
End Sub
Related
I have been trying to create a function which checks that if Col"B" <> Empty then copy the third cell which is under the same row.
I have this Data:
Where from i want to copy the Col"D" highlighted cells and paste them into same row where Col"B" <> empty.
Here is the final result. Your help will be appreciated in this regards.
Option Explicit
Sub CopyPasting()
Dim ws As Worksheet
Dim r As Long
Dim LastRow As Long
Dim n As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
LastRow = .Range("D" & .Rows.Count).End(xlUp).Row
For r = LastRow To 2 Step -2
If .Cells(r, "B") <> "" Then
.Rows(r + "D").Copy
.Rows(r + "D").PasteSpecial
n = n + 1
End If
Next
End With
End Sub
Please, try the next code:
Sub testRetOffset3()
Dim sh As Worksheet, lastR As Long, rngV As Range, rngFin As Range, c As Range
Set sh = ActiveSheet 'use here the sheet you need
lastR = sh.Range("B" & sh.rows.count).End(xlUp).row 'last sheet row
On Error Resume Next 'if not empty cells in column, it will not return the range and raise an error
Set rngV = sh.Range("B2:B" & lastR).SpecialCells(xlCellTypeConstants)
On Error GoTo 0
If rngV Is Nothing Then Exit Sub 'stop the code if run on a wrong sheet, without empty cells in column B:B
For Each c In rngV.cells 'iterate between the discontinuous range cells
If rngFin Is Nothing Then 'if the final range is not set (first time)
Set rngFin = c.Offset(3, 2) 'set the range = the Offset necessary cell
Else
Set rngFin = Union(rngFin, c.Offset(3, 2)) 'make a Union between existing range and the Offset necessary cell
End If
Next
If Not rngFin Is Nothing Then 'copy both ranges in consecutive columns
rngV.Copy sh.Range("F2")
rngFin.Copy sh.Range("G2")
End If
End Sub
It will return in columns F:G, starting from the second row. It is easy to modify the range where to return...
You can even clear the existing processed columns and return in B:C, or in another sheet.
Edited:
In order to solve the last request, please use the next code:
Sub testRetOffsetMoreRows()
Dim sh As Worksheet, lastR As Long, rngV As Range, rngFin As Range, A As Range
Set sh = ActiveSheet
lastR = sh.Range("B" & sh.rows.count).End(xlUp).row
On Error Resume Next
Set rngV = sh.Range("A2:D" & lastR).SpecialCells(xlCellTypeConstants)
On Error GoTo 0
If rngV Is Nothing Then Exit Sub
For Each A In rngV.Areas 'iterate between the range areas
If rngFin Is Nothing Then
Set rngFin = A.cells(1,2).Offset(3, 3) 'use the second cell of the area
Else
Set rngFin = Union(rngFin, A.cells(1,2).Offset(3, 3))
End If
Next
If Not rngFin Is Nothing Then
rngV.Copy sh.Range("H2")
rngFin.Copy sh.Range("L2")
End If
End Sub
But take care to have continuous ranges when have a value in column B:B. Otherwise, the code may fail... The areas property will return differently.
I wasn't sure where you wanted the output, this will put it into a sheet called "Sheet2". (You'll have to make that before running the code it won't create it for you.)
Dim i As Long
Dim j As Long
Dim lr As Long
Dim srcWS As Worksheet
Dim destWS As Worksheet
Set srcWS = ThisWorkbook.Sheets("Sheet1")
Set destWS = ThisWorkbook.Sheets("Sheet2")
With srcWS
lr = .Cells(.Rows.Count, 4).End(xlUp).Row
j = 2
For i = 2 To lr
If .Cells(i, 2).Value <> "" Then
destWS.Cells(j, 1).Value = .Cells(i, 2).Value
destWS.Cells(j, 2).Value = .Cells(i, 4).Offset(2, 0).Value
j = j + 1
End If
Next i
End With
If you need the colors copied over as well then use this:
.Cells(i, 4).Offset(2, 0).Copy
destWS.Cells(j, 2).PasteSpecial xlPasteAll
instead of:
destWS.Cells(j, 2).Value = .Cells(i, 4).Offset(2, 0).Value
Currently, I have the following set of codes which is to help me run Macro1 for every cell in Column D, so long each cell is not blank/empty. However, with the codes below, it is not calling Macro1 at the last not empty/blank cell of the column. Any idea why is it so?
An update: I found that the loop ended early at Next R. It didn't proceed on. Any idea why so?
Sub testing()
Dim Rl As Long ' last row
Dim Tmp As Variant
Dim CellValue As Range
Dim R As Long ' row counter
With ThisWorkbook.ActiveSheet
'To find out how many rows in D, counting from the last cell (Not blank)
Rl = .Cells(.Rows.Count, "D").End(xlUp).Row
' work on column D
For R = 1 To Rl ' start the loop in row 1
Tmp = .Cells(R, "D").Value
If Len(Tmp) Then
Cells(R, "D").Select
Call Macro1
End If
Rl = .Cells(.Rows.Count, "D").End(xlUp).Row
Next R
End With
End Sub
Macro1:
Dim str As String
Dim ArrStr() As String
Dim i As Long
Dim y As Long
Dim RowsAdded As Boolean
RowsAdded = False
'Fill variables: str is the value of the active cell, ArrStr splits this value at the comma
str = ActiveCell.Value
ArrStr = Split(str, ", ")
'Loop through each ArrStr to populate each cell below the activecell
For i = 0 To UBound(ArrStr)
ActiveCell.Offset(i, 0).Value = ArrStr(i)
If RowsAdded = False Then
For y = 1 To UBound(ArrStr)
ActiveCell.Offset(1, 0).EntireRow.Insert xlDown
Next y
RowsAdded = True
End If
Next i
End Sub
Try the following code. You had most of it in your original post above but I think were a bit fixated on the for loop and the number of cells in column D whilst missing that you already had set the exit criteria..
Sub testing()
Dim myRow As Long: myRow = 1
With ThisWorkbook.ActiveSheet
'Exit when first non empty cell is encountered
Do While Len(.Cells(myRow, "D").Value)
Cells(myRow, "D").Select
Call macro
myRow = myRow + 1
Loop
End With
End Sub
Just putting out there in case anybody like to see how I did it. But its built upon #freeflow's answer:
Sub testing()
Dim myRow As Long: myRow = 1
With ThisWorkbook.ActiveSheet
'Exit when first non empty cell is encountered
Do While Len(.Cells(myRow, "D").Value) Or Len(.Cells(myRow, "D").Value) = 0
lastRow = Range("D65000").End(xlUp).Row
Cells(myRow, "D").Select
Call Macro1
myRow = myRow + 1
If myRow = finalRow Then
Cells(finalRow, "D").Select
Call Macro1
Exit Do
End If
Loop
End With
End Sub
I have created the following loop for comparing cell values in different worksheets.
Dim mRng, qRng As Range
Dim i, j As Range
Dim k As Integer
With Worksheets("Content")
Set mRng = .Range(.Range("D2"), .Range("D2").End(xlDown))
End With
With Worksheets("Book")
Set qRng = .Range(.Range("H2"), .Range("H2").End(xlDown))
End With
For Each i In mRng
For Each j In qRng
If i <> j Then
i.Interior.Color = vbRed
MsgBox "Data does not match, please check again!", vbOKOnly
Exit Sub
Else:
i.Interior.Color = vbGreen
End If
Next j
Next i
How can I modify the above codes so that 'i' will also loop together with 'j' each time (i.e. i2 vs j2, i3 vs j3).
Use a long not a range and iterate on that.
'you need to put as type after each variable
Dim worksheet1 As Worksheet, worksheet2 As Worksheet
Dim i As Long, lr As Long
Set worksheet1 = ActiveWorkbook.Worksheets("Content")
Set worksheet2 = ActiveWorkbook.Worksheets("Book")
With worksheet1
lr = .Cells(.Rows.Count, 4).End(xlUp).Row 'get last row
End With
With worksheet2
'make sure the second sheet isn't longer than the first
If .Cells(.Rows.Count, 8).End(xlUp).Row > i Then
lr = .Cells(.Rows.Count, 8).End(xlUp).Row
End If
End With
With worksheet1
For i = 1 To lr 'iterate the row
If .Cells(i, 4).Value <> worksheet2.Cells(i, 8).Value Then
.Cells(i, 4).Interior.Color = vbRed
MsgBox "Data does not match, please check again!", vbOKOnly
Exit Sub
Else
.Cells(i, 4).Interior.Color = vbGreen
End If
Next i
End With
I want to remove duplicates based on the text in Column I and sum the values in Column C, the data in the other columns doesn't matter.
I do not want a pivot table and I am aware they are the preferred option for this type of thing.
An example of what I'd like to achieve:
I found VBA code and tried to modify it. It doesn't delete all the lines.
Sub Sum_and_Dedupe()
With Worksheets("data")
'deal with the block of data radiating out from A1
With .Cells(1, 1).CurrentRegion
'step off the header and make one column wider
With .Resize(.Rows.Count - 1, .Columns.Count + 1).Offset(1, 0)
.Columns(.Columns.Count).Formula = "=sumifs(c:c, i:i, i2)"
.Columns(3) = .Columns(.Columns.Count).Value
.Columns(.Columns.Count).Delete
End With
'remove duplicates
.RemoveDuplicates Columns:=Array(9), Header:=xlYes
End With
.UsedRange
End With
End Sub
This should be an answer to your question.
However, code might require adaptation if the range in which you look becomes very long.
Option Explicit
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Dim LastRow As Long, LastCol As Long, a As Double, i As Long
Dim Rng As Range
Dim Cell As Variant, Estimate As Variant
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
LastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Set Rng = ws.Range(ws.Cells(2, 9), ws.Cells(LastRow, 9))
For Each Cell In Rng
i = 0
a = 0
For Each Estimate In Rng
If Estimate.Value = Cell.Value Then
i = i + 1 'Count nr of intances
a = a + ws.Cells(Estimate.Row, 3).Value 'sum booking value
If i > 1 Then
ws.Rows(Estimate.Row).Delete
i = 1
LastRow = LastRow - 1
End If
End If
Next Estimate
ws.Cells(Cell.Row, 3).Value = a 'Enter sum in booked this week
Next Cell
End Sub
You'll either need to change your current sheet name to data, or change the first two lines of this code to fit your needs. sh = the data sheet that you showed us. osh = an output sheet that this code will generate. Note also if column C or I move you can update the positions easily by changing colBooked and colEstimate. If you have more than a thousand unique estimate entries then make the array number larger than 999.
Sub summariseEstimates()
Dim sh As String: sh = "data"
Dim osh As String: osh = "summary"
Dim colBooked As Integer: colBooked = 3
Dim colEstimate As Integer: colEstimate = 9
Dim myArray(999) As String
Dim shCheck As Worksheet
Dim output As Worksheet
Dim lastRow As Long
Dim a As Integer: a = 0
Dim b As Integer
Dim r As Long 'row anchor
Dim i As Integer 'sheets
'Build summary array:
With Worksheets(sh)
lastRow = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For r = 2 To lastRow
If r = 2 Then 'first entry
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
Else
For b = 0 To a
If VBA.LCase(VBA.Replace(.Cells(r, colEstimate), " ", "")) = VBA.LCase(VBA.Replace(VBA.Split(myArray(b), ",")(0), " ", "")) Then 'match
myArray(b) = VBA.Split(myArray(b), ",")(0) & "," & VBA.Split(myArray(b), ",")(1) + .Cells(r, colBooked)
Exit For
End If
Next b
If b = a + 1 Then 'completed loop = no match, create new array item:
a = a + 1
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
End If
End If
Next r
End With
'Create summary sheet:
On Error Resume Next
Set shCheck = Worksheets(osh)
If Err.Number <> 0 Then
On Error GoTo 0
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
Err.Clear
Else
On Error GoTo 0
If MsgBox("*" & osh & "* sheet already exists. Proceed to delete and recreate?", vbOKCancel, "Summary") = vbCancel Then
Exit Sub
Else
Application.DisplayAlerts = False
Worksheets(osh).Delete
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
End If
End If
'Output to summary sheet:
With Worksheets(osh)
.Cells(1, 1).Value = "ESTIMATE"
.Cells(1, 2).Value = "BOOKED THIS WEEK"
For b = 0 To a
.Cells(b + 2, 1).Value = VBA.Split(myArray(b), ",")(0)
.Cells(b + 2, 2).Value = VBA.Split(myArray(b), ",")(1)
Next b
.Columns("A:B").AutoFit
End With
End Sub
I am looking to fill a spreadsheet with data repeating data, so 25 appointments for today, 25 appointments for tomorrow with the same name and so on for as far as possible.
Is the a simple way of filling the table where the date increases in blocks of 25?
Example of what i am trying to do
Try using this you might be able to achieve what you want ,any problems shout out
'to change the date to the next day
Public Function ExtraDay(strDate As String)
Dim tDay As Date
tDay = Format(DateAdd("d", 1, strDate), "dd/mm/yy")
ExtraDay = tDay
End Function
'gets the last used row
Function getThelastUsedRowAddress() As Integer
'Get Last Row in Worksheet UsedRange
Dim LastRow As Range, ws As Worksheet
Set ws = ActiveSheet
MsgBox ws.UsedRange.Rows(ws.UsedRange.Rows.Count).Row
getThelastUsedRowAddress = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).Row
End Function
'button command on the sheet
Private Sub CommandButton1_Click()
Dim n, t As Integer
Dim ns As String
n = getThelastUsedRowAddress()
t = n + n
ns = CStr(t)
Call getThelastUsedRow(CStr(n))
Call TheLoopRange(CStr(n) + 1, ns)
End Sub
'get the last used and paste after
Sub getThelastUsedRow(address As String)
'Get Last Row in Worksheet UsedRange
Dim LastRow As Range, ws As Worksheet
Dim numcopied As Integer
Dim numonpaper As Integer
Set ws = ActiveSheet
numcopied = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).Row
numonpaper = numcopied + 1
ws.UsedRange.Copy 'Destination:=Wst.Cells(1, 1)
'paste
Sheets("Sheet1").Range("A" & numonpaper).PasteSpecial xlPasteValues
End Sub
'loop the pasted range and change date to the next day from date
Sub TheLoopRange(rangestart As String, rangeend As String)
'rangestart,rangeend
Dim rCell As Range
Dim rRng As Range
Set rRng = Sheet1.Range("E" & rangestart & ":E" & rangeend)
For Each rCell In rRng.Cells
'MsgBox rCell.Value
rCell.Value = ExtraDay(rCell.Value)
Next rCell
End Sub
Lets as assume that:
We use Sheet1
Company column is column D
Date column is column I
Pease try:
Option Explicit
Sub Test()
Dim Lastrow As Long, i As Long
With ThisWorkbook.Worksheets("Sheet1")
Lastrow = .Cells(.Rows.Count, "D").End(xlUp).Row
For i = 2 To Lastrow
If i = 2 Then
.Cells(i, 9).Value = Date + 1
ElseIf i <> 2 And .Cells(i, 4).Value = 1 Then
.Cells(i, 9).Value = .Cells(i, 9).Offset(-1, 0).Value + 1
Else: .Cells(i, 9).Value = .Cells(i, 9).Offset(-1, 0).Value
End If
Next i
End With
End Sub