VBA loop to take value if field is blank - excel

Need some solution in VBA ->If the blank value in column A then takes value from column B.
I wrote some code, but I don't have any idea why this is not working.
dim LastR as Long
LastR = Worksheets("Sheet1").Range("BU" & Worksheets("Sheet1").Rows.Count).End(xlUp).Row
dim i as long
For i = LastR To 2 Step -1
If IsEmpty(Cells(i, "a")) Then Cells(i, "a").Value = Cells(i, "b").Value
Next i

You should check if the value is empty.
See two examples:
Dim LastR As Long
LastR = Worksheets("Sheet1").Range("BU" & Worksheets("Sheet1").Rows.Count).End(xlUp).Row
Dim i As Long
For i = LastR To 2 Step -1
'If Sheets("Sheet1").Cells(i, "a") = "" Then Sheets("Sheet1").Cells(i, 1).Value = Cells(i, 2).Value
If IsEmpty(Sheets("Sheet1").Cells(i, "a").Value) = True Then Sheets("Sheet1").Cells(i, 1).Value = Cells(i, 2).Value
Next i

Loop Through the Cells of a Column
All three versions do the same and are about equally efficient.
Option Explicit
Sub FillEmptiesConstants()
Const wsName As String = "Sheet1" ' Worksheet Name
Const fRow As Long = 2 ' First Row
Const lrCol As String = "BU" ' Last Row Column
Const lCol As String = "A" ' Lookup Column
Const dCol As String = "A" ' Destination Column
Const sCol As String = "B" ' Source Column
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, lrCol).End(xlUp).Row
If lRow < fRow Then Exit Sub
Dim r As Long
For r = fRow To lRow
If IsEmpty(ws.Cells(r, lCol)) Then
ws.Cells(r, dCol).Value = ws.Cells(r, sCol).Value
End If
Next r
End Sub
Sub FillEmptiesSimple()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets("Sheet1")
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, "BU").End(xlUp).Row
If lRow < 2 Then Exit Sub
Dim r As Long
For r = 2 To lRow
If IsEmpty(ws.Cells(r, "A")) Then
ws.Cells(r, "A").Value = ws.Cells(r, "B").Value
End If
Next r
End Sub
Sub FillEmptiesSimpleWith()
With ThisWorkbook.Worksheets("Sheet1")
Dim lRow As Long: lRow = .Cells(.Rows.Count, "BU").End(xlUp).Row
If lRow < 2 Then Exit Sub
Dim r As Long
For r = 2 To lRow
If IsEmpty(.Cells(r, "A")) Then
.Cells(r, "A").Value = .Cells(r, "B").Value
End If
Next r
End With
End Sub

Related

How can we compare two columns and copy differences from Sheet2 to Sheet1?

I have two ranges on two sheets.
I am trying to compare these two lists for differences, and copy any differences from Sheet2 to Sheet1. Here is my code. I think it's close, but something is off, because all if does is delete row 14 on Sheet1 and no different cells from Sheet2 are copied to Sheet1. What's wrong here?
Sub Compare()
Dim lastRow1 As Integer
Dim lastRow2 As Integer
Dim foundTrue As Boolean
lastRow1 = Sheets("Sheet1").Cells(Sheets("Sheet1").Rows.Count, "A").End(xlUp).Row
lastRow2 = Sheets("Sheet2").Cells(Sheets("Sheet2").Rows.Count, "E").End(xlUp).Row
For i = 2 To lastRow2
foundTrue = False
For j = 2 To lastRow1
If Sheets("Sheet2").Cells(i, 1).Value = Sheets("Sheet1").Cells(j, 1).Value Then
foundTrue = True
Exit For
End If
Next j
If Not foundTrue Then
Sheets("Sheet2").Cells(i).Copy Destination:=Sheets("Sheet1").Rows(lastRow1 - 1)
End If
Next i
Debug.Print i
End Sub
I want to end up with this.
Nothing that a debug session can't reveal.
You need to copy to lastrow + 1, not lastrow - 1.
After copying the first value, you need to somehow increase the value for lastRow1. But as you use this value as limit in your (inner) for-loop, you shouldn't modify it. So I suggest you introduce a counter variable that counts how many rows you already copied and use this as offset.
And you have some more mistakes:
Your data in sheet2 is in columns E and F, but you compare the values of column "A" (you wrote Sheets("Sheet2").Cells(i, 1).Value)
The source in your copy-command accesses is .Cells(i). In case i is 10, this would be the 10th cell of your sheet, that is J1 - not the cell E10. And even if it was the correct cell, you would copy only one cell, not two.
Obgligatory extra hints: Use Option Explicit (your variables i and j are not declared), and always use Long, not Integer.
Code could look like (I renamed foundTrue because it hurts my eyes to see True in a variable name)
Dim i As Long, j As Long
For i = 2 To lastRow2
foundValue = False
For j = 2 To lastRow1
If Sheets("Sheet2").Cells(i, 5).Value = Sheets("Sheet1").Cells(j, 1).Value Then
foundValue = True
Exit For
End If
Next j
If Not foundValue Then
addedRows = addedRows + 1
Sheets("Sheet2").Cells(i, 5).Resize(1, 2).Copy Destination:=Sheets("Sheet1").Cells(lastRow1, 1).Offset(addedRows)
End If
Next i
But this leaves a lot room for improvement. I suggest you have a look to the following, in my eyes it's much cleaner and much more easy to adapt. There is still room for optimization (for example read the data into arrays to speed up execution), but that's a different story.
Sub Compare()
Const sourceCol = "E"
Const destCol = "A"
Const colCount = 2
' Set worksheets
Dim sourceWs As Worksheet, destWs As Worksheet
Set sourceWs = ThisWorkbook.Sheets("Sheet2")
Set destWs = ThisWorkbook.Sheets("Sheet1")
' Count rows
Dim lastRowSource As Long, lastRowDest As Long
lastRowSource = sourceWs.Cells(sourceWs.Rows.Count, sourceCol).End(xlUp).Row
lastRowDest = destWs.Cells(destWs.Rows.Count, destCol).End(xlUp).Row
Dim sourceRow As Long, destRow As Long
Dim addedRows As Long
For sourceRow = 2 To lastRowSource
Dim foundValue As Boolean
foundValue = False
For destRow = 2 To lastRowDest
If sourceWs.Cells(sourceRow, sourceCol).Value = destWs.Cells(destRow, destCol).Value Then
foundValue = True
Exit For
End If
Next destRow
If Not foundValue Then
addedRows = addedRows + 1
sourceWs.Cells(sourceRow, sourceCol).Resize(1, colCount).Copy Destination:=destWs.Cells(lastRowDest, 1).Offset(addedRows)
End If
Next sourceRow
End Sub
Copy Differences (Loop)
A Quick Fix
Option Explicit
Sub Compare()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws1 As Worksheet: Set ws1 = wb.Worksheets("Sheet1")
Dim lRow1 As Long: lRow1 = ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row
Dim fRow1 As Long: fRow1 = lRow1
Dim ws2 As Worksheet: Set ws2 = wb.Worksheets("Sheet2")
Dim lRow2 As Long: lRow2 = ws2.Cells(ws2.Rows.Count, "E").End(xlUp).Row
Dim i As Long, j As Long
For i = 2 To lRow2
For j = 2 To lRow1
If ws2.Cells(i, "E").Value = ws1.Cells(j, "A").Value Then Exit For
Next j
' Note this possibility utilizing the behavior of the For...Next loop.
' No boolean necessary.
If j > lRow1 Then ' not found
fRow1 = fRow1 + 1
ws2.Cells(i, "E").Resize(, 2).Copy ws1.Cells(fRow1, "A")
End If
Next i
MsgBox "Found " & fRow1 - lRow1 & " differences.", vbInformation
End Sub

Loop rows to copy from one sheet's columns to another sheet

I need to use data from one sheet to fill in another sheet in the same workbook.
Using sheet1's data:
Column C's item will be copied to Sheet2 and any relevant information will be copied over as well.
Then Column D's item will be copied to the next row with its relevant information.
This will be repeated until all rows in Sheet1 are copied over to Sheet2.
(Note: I put this macro as a button in another sheet so I'm referencing each sheet in my code.)
NumRows = Worksheets("Sheet1").Range("C2", Range("C2").End(xlDown)).Rows.Count
' Select cell, *first line of data*.
Worksheets("Sheet1").Range("C2").Select
' Set Do loop to stop when ten consecutive empty cells are reached. (Make sure it's safely run; ignore)
j = 4
Do Until IsEmpty(ActiveCell) And IsEmpty(ActiveCell.Offset(10, 0))
For i = 2 To NumRows
j = j + 1
Worksheets("Sheet1").Cells(i, "C").Value = Worksheets("Sheet2").Cells(j, "C").Value
Worksheets("Sheet1").Cells(i, "A").Value = Worksheets("Sheet2").Cells(j, "A").Value
Worksheets("Sheet1").Cells(i, "B").Value = Worksheets("Sheet2").Cells(j, "B").Value
Worksheets("Sheet1").Cells(i, "E").Value = Worksheets("Sheet2").Cells(j, "D").Value
' New row for next item
j = j + 1
Worksheets("Sheet1").Cells(i, "D").Value = Worksheets("Sheet2").Cells(j, "C").Value
Worksheets("Sheet1").Cells(i, "A").Value = Worksheets("Sheet2").Cells(j, "A").Value
Worksheets("Sheet1").Cells(i, "B").Value = Worksheets("Sheet2").Cells(j, "B").Value
Worksheets("Sheet1").Cells(i, "E").Value = Worksheets("Sheet2").Cells(j, "D").Value
' Step down 1 row from present location.
ActiveCell.Offset(1, 0).Select
Next
Loop
Application.ScreenUpdating = True
End Sub
Your code is copying from sheet2 to sheet1.
Option Explicit
Sub Macro1()
Dim j As Long, i As Long, c As Long
Dim ws2 As Worksheet, lastrow As Long
Set ws2 = Sheets("Sheet2")
j = 1
Application.ScreenUpdating = False
With Sheets("Sheet1")
lastrow = .Cells(.Rows.Count, "C").End(xlUp).Row
For i = 2 To lastrow
For c = 3 To 4
If Len(.Cells(i, c)) > 0 Then
j = j + 1
ws2.Cells(j, "A") = .Cells(i, "A")
ws2.Cells(j, "B") = .Cells(i, "B")
ws2.Cells(j, "C") = .Cells(i, c)
ws2.Cells(j, "D") = .Cells(i, "E")
End If
Next
Next
End With
Application.ScreenUpdating = True
MsgBox j-1 & " rows copied", vbInformation
End Sub
Unpivot Data
The title says it all: it's a job for Power Query. Yet, here's my stab at VBA.
Option Explicit
Sub UnpivotData()
Const ProcTitle As String = "Unpivot Data"
Const sName As String = "Sheet1"
Dim ssCols As Variant: ssCols = VBA.Array(1, 2, 5)
Dim smCols As Variant: smCols = VBA.Array(3, 4)
Const dName As String = "Sheet2"
Const dFirst As String = "A2"
Dim dsCols As Variant: dsCols = VBA.Array(1, 2, 4)
Const dmCol As Long = 3
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim strg As Range: Set strg = sws.Range("A1").CurrentRegion
Dim srCount As Long: srCount = strg.Rows.Count - 1 ' no headers
Dim sdrg As Range: Set sdrg = strg.Resize(srCount).Offset(1)
Dim sdData As Variant: sdData = sdrg.Value
Dim drCount As Long: drCount = srCount * (UBound(smCols) + 1)
Dim dcCount As Long: dcCount = UBound(dsCols) + 2
Dim ddData As Variant: ReDim ddData(1 To drCount, 1 To dcCount)
Dim sdValue As Variant
Dim sr As Long
Dim sc As Long
Dim c As Long
Dim dr As Long
For sr = 1 To srCount
For sc = 0 To UBound(smCols)
sdValue = sdData(sr, smCols(sc))
If Not IsError(sdValue) Then
If Len(CStr(sdValue)) > 0 Then
dr = dr + 1
ddData(dr, dmCol) = sdValue
For c = 0 To UBound(ssCols)
ddData(dr, dsCols(c)) = sdData(sr, ssCols(c))
Next c
'Else ' blank value
End If
'Else ' error value
End If
Next sc
Next sr
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dfCell As Range: Set dfCell = dws.Range(dFirst)
Dim ddrg As Range: Set ddrg = dfCell.Resize(dr, dcCount)
ddrg.Value = ddData
MsgBox "Data copied.", vbInformation, ProcTitle
End Sub
I believe you wanna copy data from sheet1 to sheet2 down the line of sheet to data, not sure about asking overriding the data on sheet to, we can create script without looping, please find below if it is helpful.
Sub Copydata()
Dim I As Long
Sheet1.Select
I = Range("C10000").End(xlUp).Row
Range("C2:C" & I).Select
Range(Selection, Selection.End(xlToRight)).Copy
Sheet2.Select
Range("C10000").End(xlUp).Offset(1, 0).Select
ActiveCell.PasteSpecial
End Sub

Duplicate row if a cell within a column contains text, then move values from one column to another?

I am trying to build a macro that goes through my data set and checks if there's any text in column W, if it does I would like the macro to duplicate the row beneath it, then move the values from Columns X and W to U and Q respectively.
My code at the moment is only trying to get the duplicate part down but its not working and I'm kind of stuck, could you have a look at it and help out?
Dim lastRow2 as Long
Dim cel as Range, srchRng as Range
lastRow2 = Worksheets("UPLOAD COPY").Cells(Rows.Count, 23).End(xlUp).Row
Set srchRng = Range("W2: W" & lastRow2)
For Each cel In srchRng
If InStr(1, cel.Value, "*") > 0 Then
cel.Offset(1).Insert
cel.EntireRow.Copy cel.Offset(1)
Set cel = cel.Offset(2)
End If
Next cel
Create Duplicate Rows
Option Explicit
Sub createDuplicateRows()
Const wsName As String = "UPLOAD COPY"
Const FirstRow As Long = 2
Const Col As Variant = "W" ' or 23
Dim OldCols As Variant: OldCols = Array("W", "X") ' or 23, 24
Dim NewCols As Variant: NewCols = Array("Q", "U") ' or 17, 21
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim LastRow As Long: LastRow = ws.Cells(ws.Rows.Count, Col).End(xlUp).Row
Dim CurrentRow As Long: CurrentRow = FirstRow
Dim j As Long
Do
If ws.Cells(CurrentRow, Col).Value <> "" Then GoSub insertRow
CurrentRow = CurrentRow + 1
Loop Until CurrentRow > LastRow
Exit Sub
insertRow:
ws.Rows(CurrentRow + 1).EntireRow.Insert Shift:=xlDown
ws.Rows(CurrentRow).EntireRow.Copy ws.Rows(CurrentRow + 1)
CurrentRow = CurrentRow + 1
GoSub changeValues
LastRow = LastRow + 1
Return
changeValues:
For j = 0 To UBound(OldCols)
ws.Cells(CurrentRow, NewCols(j)).Value _
= ws.Cells(CurrentRow, OldCols(j)).Value
ws.Cells(CurrentRow, OldCols(j)).ClearContents
Next j
Return
End Sub
EDIT:
You can write the 'delete part' in a separate subroutine. Then you can do what I suggested in the comments. Sorry, I didn't realize that previously it would copy the already cleared (empty) values.
Option Explicit
Sub createDuplicateRows()
Const wsName As String = "UPLOAD COPY"
Const FirstRow As Long = 2
Const Col As Variant = "W" ' or 23
Dim OldCols As Variant: OldCols = Array("W", "X", "X") ' or 23, 24, 24
Dim NewCols As Variant: NewCols = Array("Q", "U", "Y") ' or 17, 21, 25
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim LastRow As Long: LastRow = ws.Cells(ws.Rows.Count, Col).End(xlUp).Row
Dim CurrentRow As Long: CurrentRow = FirstRow
Dim j As Long
Do
If ws.Cells(CurrentRow, Col).Value <> "" Then GoSub insertRow
CurrentRow = CurrentRow + 1
Loop Until CurrentRow > LastRow
Exit Sub
insertRow:
ws.Rows(CurrentRow + 1).EntireRow.Insert Shift:=xlDown
ws.Rows(CurrentRow).EntireRow.Copy ws.Rows(CurrentRow + 1)
CurrentRow = CurrentRow + 1
GoSub changeValues
LastRow = LastRow + 1
Return
changeValues:
For j = 0 To UBound(OldCols)
ws.Cells(CurrentRow, NewCols(j)).Value _
= ws.Cells(CurrentRow, OldCols(j)).Value
Next j
GoSub deleteValues
Return
deleteValues:
For j = 0 To UBound(OldCols)
ws.Cells(CurrentRow, OldCols(j)).ClearContents
Next j
Return
End Sub

How to return cell value with For Next loop using 2 sheets and multiple conditions - VBA

I'm attempting to match values between two sheets and if found and the conditions are met, perform the action of changing the cell colour.
PROBLEM:
I'm getting an error with my For...Next loop, even though I thought I have a NEXT for each FOR statement. Not sure what I've done wrong.
Also, I'm not sure my counters are setup correctly to accurately scan through each sheet/column needed. Any help would be appreciated.
Sub ReadData()
Dim wb As Workbook
Dim ws As Worksheet
Dim lastrow As Long
Dim i As Long
Set wb = ActiveWorkbook
Set ws = wb.Sheets("Ref1")
Set ws2 = wb.Sheets("TRA")
lastrow = Sheets("Ref1").Cells(Rows.Count, "A").End(xlUp).Row
lastrow2 = Sheets("TRA").Cells(Rows.Count, "A").End(xlUp).Row
Sheets("Ref1").Activate
i = 2
k = 2
For i = 2 To lastrow
For k = 2 To lastrow2
If Cells(i, 4).Value = "Active" Then
If ws.Cells(i, 18).Value = ws2.Cells(i, 1).Value And (ws2.Cells(i, 23).Value <> "Cancelled" Or ws2.Cells(i, 23).Value <> "Completed") Then
Cells(i, 20).Interior.ColorIndex = 9
End If
Next
Next
End Sub
Quick Repair
To better understand the code, it is often preferable to use letters,
instead of numbers, for columns.
The Code
Sub ReadData()
Dim wb As Workbook
Dim ws As Worksheet
Dim ws2 As Worksheet
Dim lastrow As Long
Dim lastrow2 As Long
Dim i As Long
Dim k As Long
' Use ThisWorkbook instead of ActiveWorkbook, if the code is
' in the same workbook where these sheets are.
With ActiveWorkbook
Set ws = .Worksheets("Ref1")
Set ws2 = .Worksheets("TRA")
End With
lastrow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
lastrow2 = ws2.Cells(ws2.Rows.Count, "A").End(xlUp).Row
For i = 2 To lastrow
If ws.Cells(i, "D").Value = "Active" Then
For k = 2 To lastrow2
If ws.Cells(i, "R").Value = ws2.Cells(k, "A").Value _
And ws2.Cells(k, "W").Value <> "Cancelled" _
And ws2.Cells(k, "W").Value <> "Completed" Then
ws.Cells(i, "T").Interior.ColorIndex = 9 ' Brown
Exit For
End If
Next
End If
Next
End Sub

Cut-Paste copies only one row and not all the rows

I am trying to match all the cells of the "M" column in Sheet1 and Sheet3, and copy and delete all the rows from Sheet1 that contain any value from Sheet3's "M" column.
Also, I want the records to get copied into "Sheet2" (all records to be deleted).
However, it is deleting all the records but copying only the first row and not all the required rows.
Below is the code:
Sub DeleteRows()
Dim rng As Range
Dim r As Long
Dim lr1 As Long
Dim lr3 As Long
Dim str As Variant
Dim i As Long: i = 1
Application.ScreenUpdating = False
lr3 = Sheets("Sheet3").Cells(Rows.Count, "M").End(xlUp).Row
Set rng = Sheets("Sheet3").Range("M2:M" & lr3)
lr1 = Sheets("Sheet1").Cells(Rows.Count, "M").End(xlUp).Row
For r = lr1 To 2 Step -1
str = Sheets("Sheet1").Cells(r, "M")
If Application.WorksheetFunction.CountIf(rng, str) > 0 Then
Sheets("Sheet1").Range(Cells(r, "A"), Cells(r, "N")).Cut Sheets("Sheet2").Cells(i, "A")
Sheets("Sheet1").Range(Cells(r, "A"), Cells(r, "N")).Delete (xlShiftUp)
i = i + 1
End If
Next r
Application.ScreenUpdating = True
End Sub
Well here's your almost exact same code just added With blocks and .'s because that might've been the problem
Sub DeleteRows()
Dim rng As Range
Dim r As Long
Dim lr1 As Long
Dim lr3 As Long
Dim str As Variant
Dim i As Long: i = 1
Application.ScreenUpdating = False
With ThisWorkbook.Worksheets("Sheet3")
lr3 = .Cells(.Rows.Count, "M").End(xlUp).Row
Set rng = .Range("M2:M" & lr3)
End With
With ThisWorkbook.Worksheets("Sheet1")
lr1 = .Cells(.Rows.Count, "M").End(xlUp).Row
For r = lr1 To 2 Step -1
str = .Cells(r, "M").Value
If Application.WorksheetFunction.CountIf(rng, str) > 0 Then
Sheets("Sheet2").Range(Sheets("Sheet2").Cells(i, "A"), Sheets("Sheet2").Cells(i, "N")).Value = _
.Range(.Cells(r, "A"), .Cells(r, "N")).Value
.Range(.Cells(r, "A"), .Cells(r, "N")).Delete (xlShiftUp)
i = i + 1
End If
Next r
End With
Application.ScreenUpdating = True
End Sub

Resources