Looping through a range. Copy if equals value. Runs but no results - excel

I have tried using Offset to copy and paste plus about a million other things. This used to have about ten ElseIf's that I commented out to try and simplify to help me figure out. The only other thing I could think of is that I am having a brain cramp on this so any help would be appreciated!
Sub areax()
Dim Rng1 As Range, Rng2 As Range, Rng3 As Range, Rng4 As Range
Dim Lr As Long
For Lr = Cells(Cells.Rows.Count, "B").End(xlUp).Row To 6 Step -1
If Cells(Lr, "B") <> 0 Then
If Cells(Lr, "B") = 6 Then
Set Rng1 = Range("E" & ActiveCell.Row & ":I" & ActiveCell.Row)
Set Rng2 = Range("E" & Rows.Count).End(xlUp).Offset(1)
Rng1.COPY Rng2
Application.CutCopyMode = False
Else
If Cells(Lr, "B") = 12 Then
Set Rng1 = Range("E" & ActiveCell.Row & ":J" & ActiveCell.Row)
Set Rng2 = Range("K" & ActiveCell.Row & ":P" & ActiveCell.Row)
Set Rng4 = Range("E" & Rows.Count).End(xlUp).Offset(1)
Rng1.COPY Rng4
Rng2.COPY Rng4
Application.CutCopyMode = False
End If
End If
End If
Next Lr
End Sub

OK sam axe - got to sign-out for this evening but try the code below.
From my last query comment to you, it assumes that col B has the same number of rows as the original data grid (9) and that we are only using columns E:J and K:P in the rebuilt grid. If these are not the case then you can make some appropriate mods.
Also the following assumptions apply, again modify to suit your context:
Assumes grid data is in same sheet as col B
Assumes this sheet is called "Data"
Assumes rebuilt grid data starts at original data grid start col
I have used a few more variables so that you have got flexibility to easily change your output/layout etc. I have also made two 'replacements' in your code. 1. replaced IF THEN construct with SELECT CASE construct which will allow you to add other conditions and 2. replaced your test for col B value being greater than 0 with a test for it being a numeric value. It shouldn't then crash if a string accidentally creeps in.
#Steffen Sylvest Neilson has graciously acknowledged the Dim comment and has helpfully provided a link for you to perhaps explore.
May not be perfect for your needs yet, due to my lack of understanding, but as stated earlier, should be a good starter for you.
PS An explanation why you did not appear to be copying anything could be that ActiveCell may be selected outside your data. ActiveCell doesn't follow your loop counter.
Sub areax()
Dim Rng1 As Range, Rng2 As Range
Dim lBrow As Long, lGridRow As Long, c As Long
Dim sdRow As Long, sdCol As Long
Dim gridsRow As Long, gridsCol As Long
'data start r/c
sdRow = 6
sdCol = 2
'grid start r/c
gridsRow = 6
gridsCol = 5
With Sheets("Data")
lBrow = .Cells(Rows.Count, sdCol).End(xlUp).Row
'for each row in col B
For c = sdRow To lBrow
If IsNumeric(.Cells(c, "B")) Then
'set next available row at bottom of grid
lGridRow = .Cells(Rows.Count, gridsCol).End(xlUp).Row + 1
'test col B cell value
Select Case .Cells(c, sdCol)
Case Is = 6
Set Rng1 = .Range(.Cells(c, "E"), .Cells(c, "J"))
Rng1.Copy Destination:=.Cells(lGridRow, gridsCol)
Application.CutCopyMode = False
Case Is = 12
Set Rng1 = .Range(.Cells(c, "E"), .Cells(c, "J"))
Set Rng2 = .Range(.Cells(c, "K"), .Cells(c, "P"))
Rng1.Copy Destination:=.Cells(lGridRow, gridsCol)
'add 1 to last grid row because of double-copy
lGridRow = lGridRow + 1
Rng2.Copy Destination:=.Cells(lGridRow, gridsCol)
End Select
End If
Next c
Application.CutCopyMode = False
End With
End Sub

Related

VBA - Highlight/Delete row if Range is Empty

I have a range of data, with CASE ID's in Column A, and Issues (1 through 10, or Columns B through K) in Columns B onwards.
Once certain issues are ruled out as 'normal', they would be removed from the Issues sheet based on their respective column. For ex: CASE ID #25, Issue 4 is ruled OK, then it would be deleted from Row 25, Column 5 (or Column E) but the CASE ID would remain.
The goal is that by doing this check after the fact, it may leave certain rows entirely blank, from Column B onwards (since the CASE ID would already be there.)
My code doesn't function successfully. Once run, it highlights several rows that are not entirely blank in the target range.
I'm trying to pinpoint rows in the range B2:P & lastrow where the entire row is blank, and then highlight these rows and subsequently delete them.
Code:
Public Sub EmptyRows()
lastrow = Sheets("Issues").Cells(Rows.Count, "A").End(xlUp).row
On Error Resume Next
Sheets("Issues").Activate
For Each rng In Range("B2:P" & lastrow).Columns
rng.SpecialCells(xlCellTypeBlanks).EntireRow.Interior.ColorIndex = 11
'rng.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Next rng
Application.ScreenUpdating = True
End Sub
The purpose of first highlighting is to test the code works. If successful, they would be deleted entirely.
Your description says Columns B through K, but your code has B through P...
You can do it like this (adjust resize for actual columns involved):
Public Sub EmptyRows()
Dim lastRow As Long, sht As Worksheet, c As Range, rngDel As Range
Set sht = Sheets("Issues")
For Each c In sht.Range(sht.Range("A2"), sht.Cells(Rows.Count, 1).End(xlUp)).Cells
If Application.CountA(c.Offset(0, 1).Resize(1, 10)) = 0 Then
'build range to delete
If rngDel Is Nothing Then
Set rngDel = c
Else
Set rngDel = Application.Union(rngDel, c)
End If
End If
Next c
'anything to flag/delete ?
If Not rngDel Is Nothing Then
rngDel.EntireRow.Interior.ColorIndex = 11
'rngDel.EntireRow.Delete '<< uncomment after testing
End If
End Sub
Once run, it highlights several rows that are not entirely blank in the target range.
This is because you are selecting all blanks, instead of only rows where the entire row is blank.
See the code below
Public Sub EmptyRows()
With Sheets("Issues")
lastrow = .Cells(Rows.Count, "A").End(xlUp).row
Dim rng as Range
For Each rng In .Range("B2:B" & lastrow)
Dim blankCount as Integer
blankCount = Application.WorksheetFunction.CountA(rng.Resize(1,.Range("B:P").Columns.Count))
If blankCount = .Range("B" & lastRow & ":P" & lastRow).Columns.Count Then
Dim store as Range
If store Is Nothing Then Set store = rng Else: Set store = Union(rng, store)
End If
Next rng
End With
store.EntireRow.Interior.ColorIndex = 11
'store.EntireRow.Delete
End Sub
Gathering the ranges first and then modified them (changing color or deleting) will help to execute the code faster.
Here is another approach, using CountA
For Each cell In Range("A2:A" & Cells(Rows.Count, "A").End(xlUp).Row)
Dim rng As Range
Set rng = Range("A" & cell.Row & ":" & "P" & cell.Row)
If Application.WorksheetFunction.CountA(rng) = 1 Then
rng.EntireRow.Interior.ColorIndex = 11
End If
Next cell

use range object as part of a loop

I pasted the entire macro below but this is the important part.
Range("B2:B10000").Value = Range("B2").Offset(-1, 1).Value
Range("D2:D10000").Value = Range("D2").Offset(-1, 1).Value
Range("F2:F10000").Value = Range("F2").Offset(-1, 1).Value
Range("H2:H10000").Value = Range("H2").Offset(-1, 1).Value
It works as is except it is creating unnecessary data because I don't know how to use variable names in a range object. My ranges are currently hard coded such as ("A1:A1000"), when I would like it to be something like ("A1:A & LastRow).
Also I have to explicitly call out column names to copy because the range won't accept a variable name like ("currentColumn & 1:currentColumn & LastRow).
Is there a way to use a varible name as part of a range object so we can use them in loops?
Sub prepareWorkbook()
Dim wbk As Workbook
Set wbk = ThisWorkbook
Dim wks As Worksheet
Set wks = wbk.ActiveSheet
Dim colx As Long
Dim ColumnCount As Long
Dim MySheetName As String
MySheetName = "Import"
LastRow = sht.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
'copy the worksheet and rename it before editing
Sheets(1).Copy After:=Sheets(1)
ActiveSheet.Name = MySheetName
'identify the Id column and move it to 1st column
Dim answer As Variant
Dim IdColumn As Range
answer = Application.InputBox("Enter Letter of Id column")
If Columns(answer).Column = 1 Then
Else
'cut Id column from current location and insert it at column index 1
Columns(answer).Select
Selection.Cut
Columns("A:A").Select
Selection.Insert Shift:=xlToRight
End If
'trim the PartNumber column of any trailing spaces
Dim c As Range
For Each c In Range("A1:A10000")
c.Value = Application.Trim(Replace(c.Value, Chr(160), Chr(32)))
Next
' insert column every other column
' Loop through number of columns.
ColumnCount = Application.WorksheetFunction.CountA(Rows(1)) * 2
'step 2 means skip every other
For colx = 2 To ColumnCount Step 2
Columns(colx).Insert Shift:=xlToRight
Next
Range("B2:B10000").Value = Range("B2").Offset(-1, 1).Value
Range("D2:D10000").Value = Range("D2").Offset(-1, 1).Value
Range("F2:F10000").Value = Range("F2").Offset(-1, 1).Value
Range("H2:H10000").Value = Range("H2").Offset(-1, 1).Value
wks.Cells.EntireColumn.AutoFit
MsgBox ("Done")
End Sub
Assuming the you are running code in the Worksheet added here:
'copy the worksheet and rename it before editing
Sheets(1).Copy After:=Sheets(1)
ActiveSheet.Name = MySheetName
Also not sure what is the purpose of this code, nevertheless using it for the sample
Range("B2:B10000").Value = Range("B2").Offset(-1, 1).Value
Try this:
Dim lLastRow As Long
lLastRow = wbk.Worksheets(MySheetName).UsedRange.SpecialCells(xlLastCell).Row
Rem This updates only columns B, D, F & H - adjust as needed
For colx = 2 To 8 Step 2
With wbk.Worksheets(MySheetName)
Rem Creates Range as Range(Cells(rIni,cIini), Cells(rEnd,cEnd))
rem Corresponding code for "Range("B2:B10000").Value = Range("B2").Offset(-1, 1).Value" (see comment above)
Range(.Cells(2, colx), .Cells(lLastRow, colx)) = .Cells(2, colx).Offset(-1, 1).Value
End With: Next
Something like:
Dim LastRow As Long
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("B2:B" & LastRow).Value = Range("B2").Offset(-1, 1).Value
Range("D2:D" & LastRow).Value = Range("D2").Offset(-1, 1).Value
Range("F2:F" & LastRow).Value = Range("F2").Offset(-1, 1).Value
Range("H2:H" & LastRow).Value = Range("H2").Offset(-1, 1).Value
Although this answer won't be applied to your situation, I feel like this could help answer some questions you have in there.
When specifying a range, you can separate the column (letter) and row (number) and use your own variables.
In a for loop, this could look like
for i = 1 to 100
Range("A" & i).Value = Range("A"&i).Offset(, 1).Value
next
You can also determine the number of the row of the selected cell using:
dim RowNb as long
RowNb = (ActiveCell.Row)
This also applies to columns, and can be used in a loop like I mentionned at the start.
The one thing that was conspicuous by its absence in your description was any mention of the nature of the data in the worksheet. You mentioned A1 briefly but your range value assignments started at row 2 so it may be inferred that row 1 contains column header labels.
Sub prepareWorkbook()
Dim wbk As Workbook, wks As Worksheet
Dim colx As Long
Dim lc As Long, lr As Long
Dim MySheetName As String
Set wbk = ThisWorkbook 'no idea what this does
Set wks = wbk.ActiveSheet 'no idea what this does
MySheetName = "Import"
'no idea what this does or what sht is
'LastRow = sht.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
'copy the worksheet and rename it before editing
Sheets(1).Copy After:=Sheets(1)
With Sheets(2)
.Name = MySheetName
If CBool(Application.CountIf(.Rows(1), "PartNumber")) Then
colx = Application.Match("PartNumber", .Rows(1), 0)
Else
colx = .Range(Application.InputBox("Enter Letter of Id column") & 1).Column
End If
If .Columns(colx).Column > 1 Then
'cut Id column from current location and insert it at column index 1
.Columns(colx).Cut
.Columns(1).Insert Shift:=xlToRight
End If
'quickest way to trim trailing spaces is with Text-to-Columns, Fixed Width
With .Columns(1)
.TextToColumns Destination:=.Cells(1), DataType:=xlFixedWidth, FieldInfo:=Array(0, 1)
End With
' insert column every other column (working backwards toward A1)
For lc = .Cells(1, Columns.Count).End(xlToLeft).Column To 2 Step -1
.Columns(lc).Insert Shift:=xlToRight
Next lc
For lc = (.Cells(1, Columns.Count).End(xlToLeft).Column - 1) To 2 Step -2
'let's put the row-by-row value in instead of a single value into all cells
lr = .Cells(Rows.Count, lc + 1).End(xlUp).Row
With .Cells(2, lc).Resize(lr - 1, 1)
.Cells = .Offset(-1, 1).Value
.EntireColumn.AutoFit
End With
Next lc
End With
Set wbk = Nothing
Set wks = Nothing
End Sub
Explanations as comments in code.

Set a variable as column range

I have a named range for an entire column named DAY.
I have a macro that sets pagebreaks every time a cell's value in the DAY column changes (when changing from day 1, to day 2, or day 3, there will be a page break for printing).
The macro specifies the column by letter, like "A" or "B" or "C" or "H".
How can I specify the "DAY" named range so if it moves, the code doesn't break?
Attention to:
For Each c In Range("C1:C" & lastrow)
I want to change Range("C1:C"to Range("DAY".
This breaks in various syntax forms I tried.
Sub Set_PageBreaks_DAY()
Dim lastrow As Long, c As Range
Dim i As Integer, rngData As Range
Set rngData = Range("A1").CurrentRegion
i = Application.WorksheetFunction.Match("DAY", Range("A1:AZ1"), 0)
lastrow = Cells(Rows.Count, i).End(xlUp).Row
Application.ScreenUpdating = False
ActiveSheet.ResetAllPageBreaks
For Each c In Range("C1:C" & lastrow)
If c.Offset(1, 0).Value <> c.Value And c.Offset(1, 0) <> "" Then
c.Offset(1, 0).PageBreak = xlPageBreakManual
End If
Next c
Application.ScreenUpdating = True
End Sub
First, it is important to note that Named ranges have 2 possible scopes which will affect how to access it. If your named range has workbook scope, then you should use
Dim Named_range_day as Range
Set Named_range_day = ThisWorkbook.Names("Day").RefersToRange
If the named range has worksheet scope, then use
Dim Named_range_day as Range
Set Named_range_day = wksht.Names("Day").RefersToRange
where wksht is the worksheet variable for the worksheet containing the named range.
The reason JLILI Aman's answer didn't work is you have to convert the column index number to a column letter first using
columnLetter = Split(Columns(i).Address(), "$")(2)
So for example
Sub Set_PageBreaks_CREW()
Dim lastrow As Long, c As Range
Dim i As Integer, rngData As Range
Set rngData = Range("A1").CurrentRegion
i = Application.WorksheetFunction.Match("DAY", Range("A1:AZ1"), 0)
lastrow = Cells(Rows.Count, i).End(xlUp).Row
Application.ScreenUpdating = False
ActiveSheet.ResetAllPageBreaks
columnLetter = Split(Columns(i).Address(), "$")(2)
Var = columnLetter & "1:" & columnLetter
For Each c In Range(Var & lastrow)
If c.Offset(1, 0).Value <> c.Value And c.Offset(1, 0) <> "" Then
c.Offset(1, 0).PageBreak = xlPageBreakManual
End If
Next c
Application.ScreenUpdating = True
End Sub
Range("DAY").Resize(lastrow,1)
The above will reference the cell with name DAY and lastrow rows below it and in one column.
In general to reference a table of 100 rows and 5 columns with the top left at a cell, for example G2 use
Range("G2").Resize(100,5)
the above is entirely equivalent to
Range("G2:K101")
buy you don't have to do any of the weird string math with Range("G2:K" & count+1) etc.

Application.Match very slow, copy and paste also used?

Hi all I am using the script below to check a number of columns against column A, However it is extremely slow and I was wondering if anyone knows of a quicker method.
In here I have a range of cells on different sheets being compared, once the comparison is made a check mark is made in the adjacent column and it is copied and pasted into a final sheet (possibly another slowing process) I cant think of a way to transpose without copying and pasting?
Sub CompareAndMove()
Dim rng1 As Range, rng2 As Range, i As Long, k As Long, kL As Long, iL As Long, var As Variant, y As Workbook, lRows As Long
lRows = Sheets("COMPARE").Cells(Rows.Count, 1).End(xlUp).Row
iL = Sheets("COMPARE").Range("A" & Rows.Count).End(xlUp).Row
For j = 3 To 4
For i = 2 To iL
Set rng1 = Sheets("COMPARE").Range("A" & i)
Set rng2 = Sheets("COMPARE").Columns(j)
var = Application.Match(rng1.Value, rng2, 1)
If Not IsError(Application.Match(rng1.Value, rng2, 0)) Then bln = True
If bln = True Then
rng1.Interior.Color = RGB(255, 255, 0)
rng1.Copy
rng1.Offset(0, 1).Font.Name = "Wingdings"
rng1.Offset(0, 1).Value = ChrW(&HFC)
End If
End If
Next i
Sheets("COMPARE").Range(Cells(1, 2), Cells(lRows, "B")).Copy
Sheets("COMPAREFINAL").Range("A" & Rows.Count).End(xlUp).Offset(1).EntireRow.PasteSpecial Transpose:=True
Next j
kL = Sheets("COMPARE").Range("A" & Rows.Count).End(xlUp).Row
lRows = Sheets("COMPAREOBD").Cells(Rows.Count, 1).End(xlUp).Row
For j = 3 To 4
For k = 2 To kL
Set rng1 = Sheets("COMPAREOBD").Range("A" & i)
Set rng2 = Sheets("COMPAREOBD").Columns(j)
var = Application.Match(rng1.Value, rng2, 1)
If Not IsError(Application.Match(rng1.Value, rng2, 0)) Then bln = True
If bln = True Then
rng1.Interior.Color = RGB(255, 255, 0)
rng1.Copy
rng1.Offset(0, 1).Font.Name = "Wingdings"
rng1.Offset(0, 1).Value = ChrW(&HFC)
End If
End If
Next k
Set rng1 = Nothing
Set rng2 = Nothing
Sheets("COMPAREOBD").Range(Cells(1, 2), Cells(lRows, "B")).Copy
Sheets("COMPAREFINALOBD").Range("A" & Rows.Count).End(xlUp).Offset(1).EntireRow.PasteSpecial Transpose:=True
Next j
End Sub
the main slow down here i see is that you are checking one cell at a time with the MATCH formula, if your "iL" is more than in double digits it will be in fact very slow. Is the alternative possible where you just populate a column next to your full range with the MATCH formula and work off that?

Taking a Reference cell, searching through 2nd sheet, replace data with same identifier

I decided to change my tact.
I decided to take another shot at this, but in a new way. I did a weekend long Google marathon and found I believe my answer,
Option Explicit
Sub DataUpdate()
Dim rFind As Long, NR As Long, LR As Long, LC As Long
LR = Range("C" & Rows.Count).End(xlUp).Row
LC = Cells(2, Columns.Count).End(xlToLeft).Column
NR = LR + 1
On Error Resume Next
rFind = Range("A25:A" & LR).Find(Range("A1")).Row
On Error GoTo 0
If rFind = 0 Then
If MsgBox("Customer record not found, add to dataset?", vbYesNo + vbQuestion) = vbYes Then
Range("A2", Cells(LC, 2)).Copy
Range("C" & NR).PasteSpecial xlPasteValues
Range("A1", Cells(1, LC)).ClearContents
Exit Sub
End If
Else
Range("A2", Cells(2, LC)).Copy
Range("A" & rFind).PasteSpecial xlPasteValues
Range("A1", Cells(1, LC)).ClearContents
End If
End Sub
Looking at this I just want a cleaner explanation instead of just taking it as is, and using it without knowing what I am doing.
Here is the sheet it is on:
http://dl.dropbox.com/u/3327208/Excel/Replace.zip
If I add this to my code, regurgitate this code I see I can do this, I just want to verify that this is correct.
Option Explicit
Sub PENCMR()
Dim i As Integer
With Application
.ScreenUpdating = False
End With
'Internal NCMR
Dim wsPE As Worksheet
Dim wsNDA As Worksheet
'Copy Ranges
Dim c As Variant
'Paste Ranges
Dim p As Range
'Setting Sheet
Set wsPE = Sheets("Print-Edit NCMR")
Set wsNDA = Sheets("NCMR Data")
Set p = wsPE.Range("A54:U54")
With wsPE
c = Array(.Range("AG2"), .Range("B11"), .Range("B14"), .Range("B17"), .Range("B20"), .Range("B23") _
, .Range("Q11"), .Range("Q14"), .Range("Q17"), .Range("Q20"), .Range("R25"), .Range("V23") _
, .Range("V25"), .Range("V27"), .Range("B32"), .Range("B36"), .Range("B40"), .Range("B44") _
, .Range("D49"), .Range("L49"), .Range("V49"))
End With
For i = LBound(c) To UBound(c)
p(i + 1).Value = c(i).Value
Next
With wsNDA
Dim rFind As Long, NR As Long, LR As Long, LC As Long
LR = Range("C" & Rows.Count).End(xlUp).Row
LC = Cells(2, Columns.Count).End(xlToLeft).Column
NR = LR + 1
rFind = wsNDA.Range("A:A" & LR).Find(Range("A54")).Row
Range("A54", Cells(2, LC)).Copy
Range("A" & rFind).PasteSpecial xlPasteValues
Range("A54", Cells(1, LC)).ClearContents
End With
With Application
.ScreenUpdating = True
End With
End Sub
The code runs, but it doesn't come back with an error, yet it doesn't run completely. It hits to the point where it drags everything down, then it seems to die there. Can someone help me find out why it doesn't do what I think it should do, which is copy the row, search for the number in column A, and then write over it with the correct data in row 54...
I know something is wrong, but I don't have the skills to figure out what, if someone can help me it be greatly appreciated.
I am not 100% sure of what you are trying to achieve but there are several problems in your code:
Instead of
Set p = wsPE.Range("A54:U54")
For i = LBound(c) To UBound(c)
p(i + 1).Value = c(i).Value
Next
You probably mean
Set p = wsPE.Range("A54")
For i = LBound(c) To UBound(c)
p.Offset(0, i) = c(i)
Next
In your With wsNDA block, you need to put . before the Range and Cells, for example:
.Range("A54", .Cells(2, LC)).Copy
Finally:
I would remove the ScreenUpdating statements for now, and run the code in debug mode (F8) to see step by step what the code is doing and check the values of your variables if necessary use "Add Watch"
I would avoid using a range to store temporary data. You could use a 2D array instead, like this for example:
Dim data As Variant
Redim data(1 To 1, 1 To 21) As Variant
for i = xx To yy
data(1,i+1) = c(i)
Next i
yourTargetCell.Resize(1, UBound(data,2)) = data

Resources