Excel vba - How to copy/paste when range varies - excel

Got a sheet holding 7000 rows. Data is in columns A-C. Column A is teams, B is persons, and C is towns. Row 1 holds headers.
Cell A2 is the first team name. Cell B2: C23 is persons and towns (no empty cells). However, cell A3: A23 is empty. The team name is only written out for the first row of persons/towns.
Row 24 is blank.
In A25 there is a new team name. B25:C38 is persons/towns. A26: A38 is empty.
What I want to do is to copy/paste team name in A2 down to empty cells in A3: A23. And then do the same with the team name in A25 to A26: A38. And so on down about 7000 rows for 370 teams.
But the number of rows in use for each team varies, so how can a VBA take this into account?
The only fixed information is that there is an empty row between each team/person/town-section.

I came up with a quick solution that takes into account blank lines:
Option Explicit
Sub completeTeams()
Dim i As Long
Const startDataRow = 2
Dim lastDataRow As Long
Dim lastTeamRow As Long
Dim lastTeamFound As String
Dim teamCellData As String
Dim isEmptyLine As Boolean
Rem getting the last row with data (so using column B or C)
lastDataRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "B").End(xlUp).Row
teamCellData = vbNullString
lastTeamFound = ActiveSheet.Cells(startDataRow, "A").Text
For i = startDataRow To lastDataRow
Rem trying to get the actual team name
teamCellData = ActiveSheet.Cells(i, "A").Text
Rem check to skip empty lines
isEmptyLine = Len(teamCellData) = 0 And Len(ActiveSheet.Cells(i, "B").Text) = 0
If isEmptyLine Then GoTo skipBlankLine
If Len(teamCellData) > 0 Then
lastTeamFound = teamCellData
End If
ActiveSheet.Cells(i, "A").Value = lastTeamFound
skipBlankLine:
Next
End Sub

If you're ok with a formula only approach you could add this formula to cell D2 and copy down.
=IF(B2<>"",IF(A2="",D1,A2),"")
Then copy column D and paste values into column A.

Actually wrote such a script myselft a few years ago, since a lot of analytics toolds exports information to excel like that
Select the range you want to work on, i.e A1:A7000, and run the script:
Sub fill2()
Dim cell As Object
For Each cell In Selection
If cell = "" Then cell = cell.OffSet(-1, 0)
Next cell
End Sub

Related

How do I ask VBA to go to that column based on other cell value

my current code is hardcoded and I am not sure how to refer to the cell, based on other cells. This is my current code:
Dim last as long
Dim i as long
last = Worksheets("Supermarket").Cells(Rows.Count("A").End(xlUp).row
ReDim UniqueID(0 to last) as String
If ws.Range("V4") <> "" Then
For i = 0 To last
UniqueID(i) = Worksheets("Supermarket").Range("Q2").Offset(i,0).Value 'Not always in Q2, need refer to ws
Next i
On ws, there is a row with column name for Supermarket (Cell V3), there is also a row with the column position for Supermarket (Cell V4).

How to fill blank cells in Excel with the date of 30/06/YEAR by picking the year from the cell in the next column

Consider the following table:
I have a series of blank cells with missing data. From this missing data I only have the year in the next column. I need to fill any blank cells with a standard day/month of 30/06. The year of each cell however needs to be the year in the next column. The attached file shows how my data is arranged. So at cell B 2091, the date shall be 30/06/2011 while for cell B 2098 the date shall be 30/06/2018 and at cell B 2100 the date shall be 30/06/2008.
Filter on the blank cells in column B. Then, in the topmost cell (which I'll assume to be B1 but will likely be different), enter a formula similar to the following and fill down
=DATE(C1,6,30)
where the row number in C1 is the same as your first row of data.
You can achieve this with a helper column (any blank column in the same worksheet where you need the dates). In that column enter this formula in the first cell (here in row 2) and copy down.
=IF(ISBLANK(B2),DATE(C2,6,30),B2)
Then copy the Values from the helper column to the date column and delete the helper.
Below is a small macro that is doing the same job. It needs no helper column and over-writes your existing blanks. Before you run it make sure to check the values of the 2 constants at the top and the name of the worksheet (especially the latter!) against your requirements.
Sub WriteStandardDate()
'293
Const FirstDataRow As Long = 2 'change to suit
Const DateClm As Long = 2 'change to suit
' year column must be adjacent to DateClm
Dim R As Long
Dim Arr As Variant
Dim Rng As Range
With Worksheets("Sheet1") ' change name as required
Set Rng = .Range(.Cells(FirstDataRow, DateClm), _
.Cells(.Rows.Count, DateClm).End(xlUp)) _
.Resize(, 2)
Arr = Rng.Value
For R = 1 To UBound(Arr)
If IsEmpty(Arr(R, 1)) Then
Arr(R, 1) = DateSerial(Arr(R, 2), 6, 30)
End If
Next R
Rng.Value = Arr
End With
End Sub
Update: I used the formula suggested by Variatus: =IF(ISBLANK(B2),DATE(C2,6,30),B2) and worked fine through a helper column. There was no need to copy / paste the new dates into the Dates column. I just used the helper column as the new Dates column since full dates from the original column were not changed and got inserted in the helper column thanks to the IFBLANK portion of the formula. Thanks.

Auto Number Cells (1,2,3,etc) if the adjacent cell is not blank

I am new to VBA Excel and wondering if you can help.
I have tried typing something similar to this =IF(C10,(ROW(A10)-ROW(A$9)),"") and it works. However, the downside to this is you have to enter this formula into every cell that you want to auto populate. I am trying to find a macro code in Excel so that cells will auto number 1,2,3,etc. whenever the adjacent column contains data?
For example, when a user enters data into B1, A1 will automatically be populated to 1. Then when users enters data into B2, A2 will automatically be populated to 2 and so on. Then when users delete data from B column, adjacent column in A will not contain a number.
Enter this into A1 based on what you said:
=if(isblank(B1),"",row())
If you want this in VBA you can use the following, but this assumes your data starts at Row 1. If it you want the numbering at Row 2 to start as 1, just add "-1" after the RngA part.
Sub Serial()
Dim RngA As Long, LastRow as Long 'Declares the variables used
LastRow = ActiveSheet.Cells(Cells.Rows.Count, "B").End(xlUp).Row 'This finds the last row in Column B where the loop will end.
For RngA = 1 To LastRow ' This loops from the first row to the last cell filled in column B
If Cells(RngA, 2) <> "" Then ' If Column B is blank, skip this row
Cells(RngA, 1).Value = RngA 'Column B was not blank, so put the row number in column A
End If
Next
End Sub

Macro to insert blank cells below if value >1 and copy/paste values from cell above

This site already has something similar: Copy and insert rows based off of values in a column
but the code doesn't take me quite where I need to go, and I haven't been able to tweak it to make it work for me.
My user has a worksheet with 4 columns, A-D. Column A contains specific contract numbers, column B is blank, column C has part numbers, and column D has the entire range of contract numbers. My user wants to count the number of times the entire range contract numbers has duplicates so I entered the formula =countif($D$2:$D$100000,A2) in cell E2 and copied down, giving me the number of times the specific contract in column A appears in column D. The numbers range from 1 to 11 in this workbook but the number may be higher in other workbooks this method will be used in.
The next thing I need to do is to enter blank cells below all values in column E that are greater than 1, very much like the example in the previously asked question. I then also need to copy in the same row and insert copied cells exactly to match in the same row in column A. Example: Cell E21 has the number 5 so I need to shift cells in column E only so that there are 4 blanks cells directly below it. In column A, I need to copy cell A21 and insert copied cells in four rows directly below.
Just trying to get the blank cells to insert has been a trial, using the code as given in the previous question.
Dim sh As Worksheet
Dim lo As ListObject
Dim rColumn As Range
Dim i As Long
Dim rws As Long
Set sh = ActiveSheet
Set lo = sh.ListObjects("Count")
Set rColumn = lo.ListColumns("Count").DataBodyRange
vTable = rColumn.Value
For i = rColumn.Rows.Count To 1 Step -1
If rColumn.Cells(i, 1) > 1 Then
rws = rColumn.Cells(i, 1) - 1
With rColumn.Rows(i)
.Offset(1, 0).Resize(rws, 1).Cells.Insert
.EntireRow.Copy .Offset(1, 0).Resize(rws, 1).Cells
.Offset(1, 0).Resize(rws, 1).EntireRow.Font.Strikethrough = True
End With
End If
Next
I would be very grateful for any help as I have been fighting with this monster for a week.
While this is indeed possible to do, it might be a good idea to look into moving the list of all contract numbers from column D to a different sheet. Even though it is quite simple to loop through a range and insert rows based on cell values - it'll also create holes in columns D and E.
Here's code for simply adding the rows and copying the values as you specified.
Sub Main()
'---Variables---
Dim source As Worksheet
Dim startRow As Integer
Dim num As Integer
Dim val As String
Dim i As Long
'---Customize---
Set source = ThisWorkbook.Sheets(1) 'The sheet with the data
startRow = 2 'The first row containing data
'---Logic---
i = startRow 'i acts as a row counter
Do While i <= source.Range("E" & source.Rows.Count).End(xlUp).Row
'looping until we hit the last row with a value in column E
num = source.Range("E" & i).Value 'Get number of appearances
val = source.Range("A" & i).Value 'Get the value
If num > 1 Then 'Number of appearances > 1
Do While num > 1 'Create rows
source.Range("A" & i + 1).EntireRow.Insert 'Insert row
source.Range("A" & i + 1) = val 'Set value
num = num - 1
i = i + 1 'Next row
Loop
End If
i = i + 1 'Next row
Loop
End Sub
Of course you could also remove the holes from column D after inserting the new rows and modify the formula in column E so that it remains copyable and doesn't calculate for the copied rows.
Generally it makes things easier if a single row can be thought of as a single object, as creating or deleting a row only affects that one single object. Here we have one row represent both a specific contract and a contract in the all contracts list - this could end up causing trouble later on (or it could be totally fine!)

Copy all cells with certain value into another column skipping blanks

I have three columns, A, B and C:
Column A contains names, NAME1, NAME2, etc.
Column B contains only the values "YES" or "NO".
Column C is suppose to contain the names from column A that have value "YES" in column B.
I can say that as long as the value is "YES" in column B, copy the value from column A to column C. Very simple with:
C1=IF(B1="YES",A1,"")
But this will include blank cells, which I don't want to. So I guess I am looking for a way to copy all the names from column A with value "YES" in column B and paste them into column C skipping the blanks.
I did find a VBA project that colors all the cells within a column with a certain value. I am not sure how to edit this into what I need. Here is the code I came up with so far.
ISSUES
1) Runtime Error '1004' Application-defined or Object-defined error
2) Copying from Column A
3) Check and Remove Duplicates from NewRange
EDIT 1: Added comment rows into the code
EDIT 2: Change NewRange to be made from column A with Offset (untested due to runtime error)
EDIT 3: Code for copying form one sheet separated from code for pasting into another sheet
EDIT 4: Added correction from user #abahgat
EDIT 5: Remove duplicates
Sub RangeCopyPaste()
Dim cell As Range
Dim NewRange As Range
Dim MyCount As Long
MyCount = 1
'--> Loop through each cell in column B
'--> Add each cell in column A with value "YES" in column B to NewRange
For Each cell In Worksheets("Sheet1").Range("B1:B30")
If cell.Value = "YES" Then
If MyCount = 1 Then Set NewRange = cell.Offset(0,-1)
Set NewRange = Application.Union(NewRange, cell.Offset(0,-1))
MyCount = MyCount + 1
End If
Next cell
'--> Copy NewRange from inactive sheet into active sheet
NewRange.Copy Destination:=activesheet.Range("C1")
'--> Remove Duplicates
activesheet.Range("C1:C30").RemoveDuplicates
End Sub
Solution without VBA:
column C contains formulas like:
=COUNTIF(B$1:B1;"yes")
increase number in column C if this row has "yes" value in column B.
This value will by used in next step.
column D contains formulas like:
=INDEX(A:A;MATCH(ROW();C:C;0))
take value from:
table: an entire A row
row number: calculated by match function: find first occurance of row number (row number where we will place the value) in entire C column. 0 meens that we looking for exactly this number not an clossest.
to skip errors:
=IF(ISERROR(MATCH(ROW();C:C;0));"";INDEX(A:A;MATCH(ROW();C:C;0)))
easier can be writen:
=IFERROR(INDEX(A:A;MATCH(ROW();C:C;0));"")
and this means:
write the value from rule if this value is not an error or write empty string if the rule is an error
Just used a Andcondition on your If to avoid the empty cells
In C1, put then copy down =IF(AND(LEN(A1>0),B1="YES"),A1,NA()))
Select column C
Press F5
Special ... check Formulas and then tick Errors (see pic)
Delete the selected cells, to leave you with a shorter list of desired names in column C
This will do the trick:
Sub RangeCopyPaste()
Dim cell As Range
Dim NewRange As Range
Dim MyCount As Long
MyCount = 1
For Each cell In Worksheets("Sheet1").Range("B1:B30")
If cell.Value = "YES" Then
If MyCount = 1 Then Set NewRange = cell.Offset(0,-1)
Set NewRange = Application.Union(NewRange, cell.Offset(0,-1))
MyCount = MyCount + 1
End If
Next cell
NewRange.Copy Destination:=activesheet.Range("D1")
End Sub

Resources