I have this problem in Excel that I want to solve using Macros in VBA. I have a sheet that contains data in this format:
separator
1
2
6
3
8
342
532
separator
72
28
10
21
separator
38
23
234
What I want to do is to create a VBA macro that creates a new sheet for every series of data (a series starts from the "separator" and ends before the next one or at the end of the initial sheet) and copy respective data in the new sheets.
Example:
1
2
6
3
8
342
532
in sheet1
72
28
10
21
in sheet2 etc.
Thank you very much, I appreciate it!
This copies data from beginning to the first separator ("q"):
Sub macro1()
Dim x As Integer
x = 1
Sheets.Add.Name = "Sheet2"
'Get cells until first q
Do Until Sheets("Sheet1").Range("A" & x).Value = "q"
Sheets("Sheet2").Range("A" & x).Value = Sheets("Sheet1").Range("A" & x).Value
x = x + 1
Loop
End Sub
Try this... (UNTESTED)
Const sep As String = "q"
Sub Sample()
Dim ws As Worksheet, wsNew As Worksheet
Dim lRow As Long, i As Long, rw As Long
'~~> Set this to the relevant worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
'~~> Add a new temp sheet
Set wsNew = ThisWorkbook.Sheets.Add
'~~> Set row for the new output sheet
rw = 1
With ws
'~~> Get the last row
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Loop through the cells from row 2
'~~> assuming that row 1 has a spearator
For i = 2 To lRow
If .Range("A" & i).Value = sep Then
Set wsNew = ThisWorkbook.Sheets.Add
rw = 1
Else
wsNew.Cells(rw, 1).Value = .Range("A" & i).Value
rw = rw + 1
End If
Next i
End With
End Sub
You could use this to avoid Looping every row. As long as you want to delete the original data as well.
SubSample()
Dim x As Integer
Dim FoundCell As Range
Dim NumberOfQs As Long
Dim SheetWithData As Worksheet
Dim CurrentData As Range
Set SheetWithData = Sheets("Sheet4")
NumberOfQs = WorksheetFunction.CountIf(SheetWithData.Range("A:A"), "q")
x = 1
Set FoundCell = SheetWithData.Range("A1", SheetWithData.Range("A" & Rows.Count)).Find("q", , , , , xlPrevious)
If Not FoundCell Is Nothing Then
Set LastCell = FoundCell.End(xlDown)
Set CurrentData = SheetWithData.Range(FoundCell, LastCell)
Sheets.Add.Name = "QSheetNumber" & x 'Get cells until first q
CurrentData.Cut Sheets("QSheetNumber" & x).Range("A1")
Sheets("QSheetNumber" & x).Rows(1).Delete
x = x + 1
Set FoundCell = SheetWithData.Range("A1", SheetWithData.Range("A" & Rows.Count)).Find("q", FoundCell, , , , xlPrevious)
If Not FoundCell Is Nothing Then
Set LastCell = FoundCell.End(xlDown)
Set CurrentData = SheetWithData.Range(FoundCell, LastCell)
Sheets.Add.Name = "QSheetNumber" & x 'Get cells until first q
CurrentData.Cut Sheets("QSheetNumber" & x).Range("A1")
Sheets("QSheetNumber" & x).Rows(1).Delete
x = x + 1
Else
Exit Sub
End If
Else
Exit Sub
End If
End Sub
Related
I have a macro that is working 99% of the time, but giving me trouble with one portion. I have data that is split into different size groups depending on certain parameters. The groups range from 1 row to as many at 10+. I am trying to copy each of the "groups" and paste into a template sheet and save which I've figured out.
Row Column B Column C
1 ASDF a
2 SDF a
3 WIRO a
4 VNDH a
5
6 FIJDK b
7 DFKIEL b
8
9 DLFKD c
10
11 OYPTK d
12 SSAODKJ d
13 SKJSJ d
Where I'm having trouble is Row 9 where Column b B = DLFKD and Column C = C
Desired Output:
Copy only row 9
Actual Output:
Copying Rows 9- 11
Existing Macro:
Data begins on Row 5.
Sub templatecopy()
Dim x As Workbook
Dim y As Workbook
Dim N As Long
Dim name As String
'## Open both workbooks first:
Set x = ActiveWorkbook
'Set R
R = 5
'start Loop
Do Until N = 96
Set y = Workbooks.Open("F:\Logistics Dashboard\Customs Macro\Cover Sheet Template.xlsx")
'set N
N = Range("B" & R).Cells(1, 1).End(xlDown).Row
'Now, copy Container Numbers from x and past to y(template):
x.Sheets("Sheet1").Range("B" & R & ":C" & N).Copy
y.Sheets("Sheet1").Range("A14").PasteSpecial
'save as Name of Vessel
name = "F:\Logistics Dashboard\Customs Macro\" & y.Sheets("Sheet1").Range("A14").Value & ".xlsx"
ActiveWorkbook.SaveAs Filename:=name
'Close template after saving to reset:
y.Close
'set R equal to new row to start
R = N + 2
Loop
End Sub
The issue is with how I am setting "N". Its having trouble distinguishing Row 9 where its just one row of data.
With the correct sheet selected this line of code should select the ranges on your sheet:
Thisworkbook.Worksheets("Sheet1").range("B:C").specialcells(xlcelltypeconstants,23).select
You'll need to add another line to account for formula as well as constants.
Public Sub FindRegionsOnSheet()
Dim sAddress As String
Dim aAddress() As String
Dim vItem As Variant
Dim x As Long
Dim wbTarget As Workbook
Dim wsSource As Worksheet
Dim wsTarget As Worksheet
Set wsSource = ThisWorkbook.Worksheets("Sheet1")
Set wbTarget = Workbooks.Open("F:\Logistics Dashboard\Customs Macro\Cover Sheet Template.xlsx")
Set wsTarget = wbTarget.Worksheets("Sheet1")
'Find all ranges of constant & formula values in column B:C.
With wsSource.Columns(2).Resize(, 2)
On Error Resume Next
sAddress = .SpecialCells(xlCellTypeConstants, 23).Address(0, 0) & ","
sAddress = sAddress & .SpecialCells(xlCellTypeFormulas, 23).Address(0, 0)
If Right(sAddress, 1) = "," Then sAddress = Left(sAddress, Len(sAddress) - 1)
On Error GoTo 0
End With
'Place within an array.
If Not sAddress = vbNullString Then
If InStr(1, sAddress, ",") = 0 Then
ReDim aAddress(0 To 0)
aAddress(0) = "'" & wsSource.Name & "'!" & sAddress
Else
aAddress = Split(sAddress, ",")
For x = LBound(aAddress) To UBound(aAddress)
aAddress(x) = "'" & wsSource.Name & "'!" & aAddress(x)
Next x
End If
End If
''''''''''''''''''''''''''''''''''''''''
'Not sure how what you're doing once moved to the Target book......
'Think this is correct, but test first...
''''''''''''''''''''''''''''''''''''''''
For Each vItem In aAddress
wsTarget.Cells.Clear
Range(vItem).Copy Destination:=wsTarget.Range("A14")
wbTarget.SaveCopyAs "F:\Logistics Dashboard\Customs Macro\" & wbTarget.Sheets("Sheet1").Range("A14") & ".xlsx"
Next vItem
wbTarget.Close
End Sub
The 23 in the SpecialCells indicates what types of cells to include in the result:
XlSpecialCellsValue constants Value
xlErrors 16
xlLogical 4
xlNumbers 1
xlTextValues 2
These values can be added together to return more than one type (total = 23). The default is to select all constants or formulas, no matter what the type.... so probably don't need the 23 at all.
How can I read in Visual Basic from column B, sheet "control" in Excel cell for cell till an empty cell?
After that I would like to generate for every cell a new sheet with the name from cells. In this:
:
you see the content of this column, which could be different from time to time. After reading it I want to generate empty sheets with names: RW_BONDS, ... .
You can do something like this.
Private Sub CommandButton1_Click()
Dim ws As Excel.Worksheet
Dim lRow As Long
Dim lastRow As Long
'Set the sheet to read from
Set ws = Application.Sheets("control")
'Set the row to start reading at
lRow = 3
lastRow = wsOwners.Cells(wsOwners.Rows.Count, "B").End(xlUp).Row
'Loop through the rows
Do While lRow <= lastRow
If ws.Range("B" & lRow).Value <> "" then
'Add a new sheet
ActiveWorkbook.Sheets.Add Before:=Worksheets(Worksheets.Count)
'Change the name to the value of column B in the current row
ActiveWorkbook.ActiveSheet.Name = ws.Range("B" & lRow).Value
End If
'Increment your row to the next one
lRow = lRow + 1
Loop
End Sub
Sub test()
Dim i As Long
i = 1
While Len(Sheets("Control").Cells(i, 2))
Worksheets.Add.Name = Sheets("Control").Cells(i, 2): i = i + 1
Wend
End Sub
EDIT answer for the comment:
Sub test()
Dim i As Long
i = 1
With Sheets("Control")
On Error Resume Next
Application.DisplayAlerts = False
While Len(.Cells(i, 2))
If Len(Sheets(.Cells(i, 2).Value).Name) = 0 Then Else Sheets(.Cells(i, 2).Value).Delete
Worksheets.Add.Name = .Cells(i, 2): i = i + 1
Wend
Application.DisplayAlerts = True
On Error GoTo 0
End With
End Sub
set ws = worksheets("Source")
row = 1
col = "B"
Do
row = row + 1
if ws.range(col & row).text = "" then exit do
worksheets.add.name = ws.range(col & row).text
Loop
End Sub
Sub createSheets()
With Worksheets("control")
iRow = 1 'Start on the first row
While Len(.Cells(iRow, 2)) > 0 'While there isn't a blank cell
Worksheets.Add.Name = .Cells(iRow,2) 'Create/rename sheet
iRow = iRow + 1
Wend
End With
End Sub
I'm writing a simple formatting macro to alternate the row color for a table in Excel.
I want this macro to be able to format any size table (no matter row/column size).
For example, I want the macro to work when I have a chart with 6 rows 4 columns, or 4 rows 5 columns, or 9 rows 10 columns, etc.
Here's the code I have so far - but I'm getting a runtime error.
If ActiveSheet Is Nothing = False Then
Set MyWS = ActiveWorkbook.ActiveSheet
lastCol = MyWS.UsedRange.Columns.Count + 1
lastRow = MyWS.UsedRange.Rows.Count + 1
For Each Cell In Range(lastRow, lastCol) ''change range accordingly
If Cell.Row Mod 2 = 1 Then
Cell.Interior.ColorIndex = 15 ''color to preference
Else
Cell.Interior.ColorIndex = 14 ''color to preference or remove
End If
Next Cell
End If
I've tried multiple versions of the Range - having the column var come first, having an '&' instead of a comma, etc.
If I use just Range("A1:A" & lastRow), it'll work but just for the data in column A.
I would need it to span across all columns in the chart.
If the tables are all starting from cell A1, change your for statement to:
For Each Cell In Range("A1", Cells(lastRow, lastCol)) ''change range accordingly
Though also, the way your for loop works is that it is changing every cell. It can be optimized to color the row up to the last column at once.
If ActiveSheet Is Nothing = False Then
Set MyWS = ActiveWorkbook.ActiveSheet
lastCol = MyWS.UsedRange.Columns.Count + 1
lastRow = MyWS.UsedRange.Rows.Count + 1
Dim i As Integer
For i = 1 To lastRow
If i Mod 2 = 1 Then
Range("A" & i, Cells(i, lastcol)).Interior.ColorIndex = 15
Else
Range("A" & i, Cells(i, lastcol)).Interior.ColorIndex = 14
End If
Next i
End If
Try this:
Dim r As Range
For Each r In MyWs.UsedRange.Rows
If r.Row Mod 2 = 1 Then
r.Interior.ColorIndex = 15
Else
r.Interior.ColorIndex = 14
End If
Next r
Always good to include Option Explicit in your code modules. Try the following:
Option Explicit
Sub test()
Dim MyWS As Excel.Worksheet
Dim objRow As Excel.Range
Dim lastCol As Long
Dim lastRow As Long
Dim lngRow As Long
If ActiveSheet Is Nothing = False Then
Set MyWS = ActiveWorkbook.ActiveSheet
lastCol = MyWS.UsedRange.Columns.Count + 1
lastRow = MyWS.UsedRange.Rows.Count + 1
For lngRow = 1 To lastRow
Set objRow = MyWS.Range(MyWS.Cells(lngRow, 1), MyWS.Cells(lngRow, lastCol))
If lngRow Mod 2 = 1 Then
objRow.Interior.ColorIndex = 15 'color to preference
Else
objRow.Interior.ColorIndex = 14 'color to preference or remove
End If
Next lngRow
End If
End Sub
I have data ranges in Row 1,2,3 and 4 for columns A:Q. I am trying to create a VBA so it does the following:
Copy Row 1 A:Q, drag and paste n number of rows based on cell O12 starting on Cell A17.
Copy Row 2 A:Q, drag and paste n number of rows based on cell O12 but the paste range should be after what has been pasted for Row 1 range.
Repeat for Row 3 and 4.
So for say of cell O12 states 4, i should be getting 16 rows 4 for each row dragged down.
Any help would be appreciated.
Sub CopyJournalLines()
' Works out last cell with data in columns A or B, copys row 2 and paste within that range (from startrow)
Dim ws As Worksheet
Dim rng1 As Range
Dim LastRow As String
Dim StartRow As String
Dim Copyrange As String
Dim LastYRow As String
Application.ScreenUpdating = False
' Find the last row of data on Concur Extract sheet
Set ws = Sheets("Invoicing")
Set rng1 = ws.Columns("A:B").Find("*", ws.[a1], xlValues, , xlByRows, xlPrevious)
' Setting range on Test to copy formulas accross into
StartRow = 17
LastRow = rng1.Row + 1
LastYRow = rng1.Row + 2
If LastYRow < 21 Then
LastYRow = 19
End If
Set ws = Sheets("Vision Import Sheet")
Let Copyrange = StartRow & ":" & LastRow
Let LastYCell = "AB" & LastYRow
' Clear previous content - limited to clear first 1000rows
Rows("17:5000").Cells.Clear
'Selection.ClearContents
If LastRow < 17 Then
GoTo End1
End If
' Copying & pasting row with correct formulas
Rows("1:5").Select
Selection.EntireRow.Hidden = False
Rows("1:1").Select
Selection.Copy
Rows("17:17").Select
ActiveSheet.Paste
Rows("17:17").Select
Selection.Replace What:="#", Replacement:="=", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
Rows("17:17").Select
Selection.Copy
Rows(Copyrange).Select
ActiveSheet.Paste
Rows("1:5").Select
Selection.EntireRow.Hidden = True
End1:
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
The copy/paste method should be put in two loops corresponding to two parameters: the number of lines to copy and the number of copies per line.
For the following code, you can choose to copy in the format 111222333 or in the format 123123123 by commenting and un-commenting the two lines that calculate the iCopyRow parameter.
Sub CopyJournalLines2()
Dim wsInv As Worksheet
Dim i As Integer
Dim j As Integer
Dim iStartRow As Integer
Dim iNumCopies As Integer
Dim iNumLines As Integer
Dim iCopyRow As Integer
Dim CopyRange As Range
Dim PasteRange As Range
Set wsInv = ThisWorkbook.Sheets("Invoice Upload")
With wsInv
.Rows("17:5000").Cells.Clear
iStartRow = 17
iNumCopies = .Range("O12").Value
iNumLines = .Range("P12").Value
For i = 1 To iNumLines
Set CopyRange = .Range(.Cells(i, 1), .Cells(i, 17))
iCopyRow = iStartRow + (i - 1) * iNumCopies '---Copies lines in order 111222333444 etc.
'iCopyRow = iStartRow + (i - 1) '---Copies lines in order 123412341234 etc.
Set PasteRange = .Range(.Cells(iCopyRow, 1), .Cells(iCopyRow, 17))
PasteRange.Formula = CopyRange.Formula
If iNumCopies > 1 Then
For j = 2 To iNumCopies
iCopyRow = iStartRow + j - 1 + (i - 1) * iNumCopies '---Copies lines in order 111222333444 etc.
'iCopyRow = iStartRow + i - 1 + ((j - 1) * iNumLines) '---Copies lines in order 123412341234 etc.
.Range(.Cells(iCopyRow, 1), .Cells(iCopyRow, 17)).Formula = PasteRange.Formula
Next j
End If
Next i
End With
End Sub
Private Sub CommandButton1_Click()
Dim selection As Variant
selection = UserForm1.ComboBox1.Text
Sheets("Sheet1").Select
Cells(1, 2) = selection
Sheets("Sheet1").Select
selection = Cells(1, 2)
namefile = "C:\Users\xxx\" & Left(selection, 1) & "\" & selection & ".xls"
Workbooks.Open Filename:=namefile
Dim wk1 As Workbook
Dim wk2 As Workbook
Dim sh1 As Worksheet
Dim sh2 As Worksheet
Set wk1 = Workbooks("file1.xlsm")
Set wk2 = Workbooks(selection & ".xls")
Set sh1 = wk1.Worksheets("Sheet2")
Set sh2 = wk2.Worksheets("Sheet1")
sh2.Activate
Dim firstempty As Variant
Dim x As Integer
Dim y As Integer
Dim A1 As Variant
Dim R1 As Variant
Dim C1 As Variant
x = 0
y = 0
While x = 0
If Range(Cells(1, y), Cells(1, y)) <> "" Then
y = y + 1
Else: Range(Cells(1, y), Cells(1, y)).Select
A1 = Target.Address
R1 = Target.Row
C1 = Replace(A1, R1, "")
firstempty = (C1 & ":" & C1)
x = 1
End If
Wend
With sh1
.Columns("D:D").Copy Destination:=sh2.Range(firstempty)
End With
End
End Sub
I need to copy column D of Sheet2 file1.xls on the first blank column of sheet1 of a second file whose name is selected by a combobox.
I am having trouble defining the letter of the empty column of the second file.
I am getting runtime error 424 and my debugger brings me to the point in the code:
A1 = Target.Address
What am I doing wrong?
Shouldn't y=0 be y=1 ?
Now you're referring to column 0.
And then this should work:
If Cells(1, y) <> "" Then
y = y + 1
Else
firstempty = y
x = 1
End If
And then:
.Columns("D:D").Copy Destination:=sh2.Columns(firstempty)
Here is a much faster way to get the last column:
Private Sub CommandButton1_Click()
Dim selection As Variant
selection = UserForm1.ComboBox1.Text
Sheets("Sheet1").Select
Cells(1, 2) = selection
Sheets("Sheet1").Select
selection = Cells(1, 2)
namefile = "C:\Users\xxx\" & Left(selection, 1) & "\" & selection & ".xls"
Workbooks.Open Filename:=namefile
Dim wk1 As Workbook
Dim wk2 As Workbook
Dim sh1 As Worksheet
Dim sh2 As Worksheet
Set wk1 = Workbooks("file1.xlsm")
Set wk2 = Workbooks(selection & ".xls")
Set sh1 = wk1.Worksheets("Sheet2")
Set sh2 = wk2.Worksheets("Sheet1")
Dim LastColumn As Long
LastColumn = sh2.Cells(1, Columns.Count).End(xlToLeft).Column + 1
sh1.Columns("D:D").Copy sh2.Cells(, LastColumn)
End Sub
As a side note could you explain this part of code:
Sheets("Sheet1").Select
Cells(1, 2) = selection
Sheets("Sheet1").Select
selection = Cells(1, 2)
It looks like you are getting a value then assigning the value to a cell then the cells value (that you just assigned already) back to the variable that assigned the original value.
at the most you should only need one line:
Sheets("Sheet1").Cells(1, 2) = selection
dno't get the need for the rest.