0
Private Sub CommandButton5_Click()

Dim ws As Worksheet, lRow As Integer

For Each ws In Worksheets
    If ws.Name <> "Sheet1" Then
        lRow = ws.Range("A" & Rows.Count).End(xlUp).Row
        For Each cell In Range("A2:A" & lRow)
            If cell.Value >= Date - 2 And cell.Value <= Date + 2 Then
                cell.EntireRow.Copy Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Offset(1, 0)
            End If
        Next cell
    End If
Next ws
End Sub

This code is searching in just 1st column, I want it to search in the whole sheet. How do I do that ? Any suggestions ?

braX
  • 11,506
  • 5
  • 20
  • 33
Nagendra
  • 17
  • 1
  • 4
  • `For Each cell In ws.UsedRange` - but there are faster ways to do this than looping over every single cell - also, you need to make sure the cell's value is numeric before attempting the `>=` and `<=`. – BigBen Oct 02 '19 at 14:06
  • I am very much new to macros. So, I do not know much about it. The cell values are Dates only, I have got them using conditional formatting. It would be really amazing if you could help me out here. Thanks. – Nagendra Oct 02 '19 at 14:12
  • Another note - don't you want to exclude `"Sheet2"` from your loop, i.e. `If ws.Name <> "Sheet1" And ws.Name <> "Sheet2" Then`? – BigBen Oct 02 '19 at 14:14
  • I will fix that. Thank you. – Nagendra Oct 02 '19 at 14:16
  • Are you trying to loop through all sheets but Sheet1, or just through to Sheet2? – Ama Oct 02 '19 at 16:05
  • What are you trying to achieve with this: `cell.EntireRow.Copy Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Offset(1, 0)` ? – Ama Oct 02 '19 at 16:05
  • I just want to check if there is any date within two days from the current date, if yes, then copy that entire row from Sheet 1 to Sheet 2. – Nagendra Oct 02 '19 at 18:08

5 Answers5

1

To check the whole Excel sheet is really slow if you have a lot of data.

But you can access directly to cells that contains a value, ignoring blanks and checking if the value fullfills your requirements.

For Each rng In ws.Cells.SpecialCells(xlCellTypeConstants, 1) 'change xlCellTypeConstants to xlCellTypeFormulas if your dates are formulas
    If rng.Value >= Date And rng.Value <= (Date + 2) Then
        'do what you want

    End If
Next rng

This code will check all non blank cells in the sheet. Also, notice that dates are numeric values, so if any cell contais a number, it will be treated as date even if the number is typed something like 12345.

Unless you know for sure that the only numbers in cells are dates, you should add an extra condition in your IF...Thento check if the number is really a date (maybe checking the format of the cell, or whatever).

This code should speed up the process heavily. Your actual loop check all cells, so it's time consuming.

Hope you can adapt this to your needs.

XlCellType enumeration (Excel)

1

I suggest you read the usedRange of your sheet(s) into an array (= 1 very fast statement) and then loop over the array. Pls. check my comments re copy target (sheet2) and avoiding copying the same line multiple times.

Private Sub CommandButton5_Click()

Dim ws As Worksheet
Dim lastCell As Range
Dim i As Long, j As Long, unusedRow As Long
Dim vals As Variant
Dim targetSheet As Worksheet

Set targetSheet = Sheets("Sheet2")                        'Do you really want to copy to Sheet2? Its one of the sheets you re searching........

For Each ws In Worksheets
    If ws.Name <> "Sheet1" Then
        Set lastCell = ws.Cells.Find(What:="*", After:=ws.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
                xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)
        vals = Range(ws.Cells(1, 1), lastCell).Value
        For i = 2 To UBound(vals, 1)                                'Excludes the first row of any sheet - as in your code
            For j = 1 To UBound(vals, 2)
                If IsDate(vals(i, j)) Then
                    If vals(i, j) >= Date - 2 And vals(i, j) <= Date + 2 Then
                        unusedRow = targetSheet.Cells.SpecialCells(xlCellTypeLastCell).Offset(1, 0).Row
                        ws.Rows(i).Copy targetSheet.Cells(unusedRow, 1)
                        Exit For                        'Assume you dont want to copy the same line more than once - so quit the inner loop after the first find
                    End If
                End If
            Next j
        Next i
    End If
Next ws

End Sub
Dschuli
  • 309
  • 3
  • 10
0

Like one of the other users suggested, you want to verify that you are comparing similar data types. In the example below, I've added a check to verify that the cell you are comparing is, in fact, a date. If so, then excel can handle subtracting them. Additionally, the abs(value) will handle your "before or after" 2 day time check.

As other people have said, there are other ways to handle this but this is how I'd approach checking every cell on the sheet using the method you started with.

Dim ws, copySheet As Worksheet
Dim cel, searchRange As Range

Set ws = ActiveWorkbook.Sheets("SheetYouWantToCheck")
Set searchRange = ws.UsedRange
Set copySheet = ActiveWorkbook.Sheets("SheetYouWantToCopyTo")

For Each cel In searchRange.Cells
    If IsDate(cel.Value) = True Then
         If Abs(cel.Value - Date) <= 2 Then
              copySheet.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = cel.Value
         End If
    End If
Next
RogueScion
  • 11
  • 2
  • This code is working partially for me. It is copying the date to sheet 2 but the problem is I want to copy the entire row if the condition is fulfilled. I am unable to figure that out since I am a newbie. – Nagendra Oct 02 '19 at 15:45
0

To iterate over all of the cells that contain a date, first we can use the special cell type xCellTypeLastCell to find the last cell populated with data.

Then, we loop over all the cells from the starting position of A1 (i.e. 1,1) until the last cell we found earlier.

If we find a cell with the criteria defined by the OP, we copy it to another worksheet.

Private Sub CommandButton5_Click()

Dim ws As Worksheet, xlastRow As Integer, xlastCol As Integer

For Each ws In Worksheets
    If ws.Name <> "Sheet1" Then
        xlastRow = ws.Cells.SpecialCells(xlCellTypeLastCell).Row
        xlastCol = ws.Cells.SpecialCells(xlCellTypeLastCell).Column

        Dim cell As Range
        For Each cell In Range(ws.Cells(1, 1), ws.Cells(xlastRow, xlastCol))
            If cell.Value >= Date - 2 And cell.Value <= Date + 2 Then

                Dim destSheet As Worksheet
                Set destSheet = Worksheets("Sheet2")
                cell.Copy destSheet.Range(cell.Address)

            End If
        Next cell
    End If
Next ws
End Sub
rplst8
  • 1
  • 1
  • Hi and welcome to stackoverflow. Thank you for contributing your answer. However can you please explain a little what your code does instead of just posting a block of code? It helps the poster learn about it and do it themselves. I also suggest [reading this](https://stackoverflow.com/questions/38882321/better-way-to-find-last-used-row) for reference. – Plutian Oct 02 '19 at 16:20
  • 1
    @Plutian, I added a description. – rplst8 Oct 04 '19 at 13:04
0

There are very good suggestions here, in particular for general cases where complex calculations or iterations are to be made over large sets of data:

  • Passing your data into an array instead of looping through each cell: with Office, what is time-expensive are call instantiations, and this reduces the calls from VBA to Excel to 1 large call rather than plenty of small calls.
  • Relying on a filter such as Cells.SpecialCells(xlCellTypeConstants, 1) also reduces the amount of cells to be processed.

But I believe that for your very specific case, you could do much simpler (and arguably efficient), by relying on .Find(), which will return the set of cells matching a given pattern (see documentation).

Simply search for cells whose values are equal to the 5 possibles dates: Today -2 through to Today +2 :

Private Sub CommandButton5_Click()

    Dim FoundCell as Range
    Dim ws As Worksheet

    For Each ws In Worksheets

        If ws.Name <> "Sheet1" Then

            For i = -2 to 2 

                FoundCell = ws.UsedRange.Find(Date(Now) + i,lookin:=xlValues)
                If Not FoundCell Is Nothing then
                    Something(FoundCell) 'Do Something on the first cell
                    FoundCell = .FindNext(FoundCell) ' Go to the next cell found
                End If

            Next i

        End If

    Next ws   

End Sub
Ama
  • 1,373
  • 10
  • 24