How can I generate the Excel as in the image below via a macro?
Briefly I would like to make:
numbers between a1 and b1 print to d column;
numbers between a2 and b2 print to e column;
numbers between a3 and b3 print to f column.
Columns A and B have thousands of values.
As an alternative, here is a formula solution:
=IF(ROW(D1)>INDEX($A:$B,COLUMN(D1)-COLUMN($C1),2)-INDEX($A:$B,COLUMN(D1)-COLUMN($C1),1)+1,"",INDEX($A:$B,COLUMN(D1)-COLUMN($C1),1)+ROW(D1)-1)
Though I realize that a formula solution may not be feasible based on this statement:
Columns A and B have thousands of values.
EDIT: Pure array VBA solution:
Sub tgr()
Dim ws As Worksheet
Dim rData As Range
Dim aData As Variant
Dim aResults() As Variant
Dim lMaxDiff As Long
Dim i As Long, j As Long
Dim rIndex As Long, cIndex As Long
Set ws = ActiveWorkbook.ActiveSheet
Set rData = ws.Range("A1", ws.Cells(Rows.Count, "B").End(xlUp))
lMaxDiff = Evaluate("MAX(" & rData.Columns(2).Address(external:=True) & "-" & rData.Columns(1).Address(external:=True) & ")") + 1
aData = rData.Value2
ReDim aResults(1 To lMaxDiff, 1 To rData.Rows.Count)
For i = LBound(aData, 1) To UBound(aData, 1)
If IsNumeric(aData(i, 1)) And IsNumeric(aData(i, 2)) Then
rIndex = 0
cIndex = cIndex + 1
For j = Int(aData(i, 1)) To Int(aData(i, 2))
rIndex = rIndex + 1
aResults(rIndex, cIndex) = j
Next j
End If
Next i
ws.Range("D1").Resize(UBound(aResults, 1), UBound(aResults, 2)).Value = aResults
End Sub
Only because I like puzzles:
Sub u5758()
Dim x As Long
Dim i As Long
Dim oArr() As Variant
Dim arr() As Long
Dim rng As Range
Dim ws As Worksheet
Application.ScreenUpdating = False
Set ws = ActiveSheet
x = 4
With ws
oArr = .Range(.Cells(1, 1), .Cells(.Rows.Count, 2).End(xlUp)).value
For j = LBound(oArr, 1) To UBound(oArr, 1)
ReDim arr(oArr(j, 1) To oArr(j, 2))
For i = LBound(arr) To UBound(arr)
arr(i) = i
Next i
.Cells(1, x).Resize(UBound(arr) - LBound(arr) + 1).value = Application.Transpose(arr)
x = x + 1
Next j
End With
Application.ScreenUpdating = True
End Sub
I like puzzles too.
Sub from_here_to_there()
Dim rw As Long
With Worksheets("Sheet5") '<~~ set this worksheet properly!
For rw = 1 To .Cells(Rows.Count, 1).End(xlUp).Row
If IsNumeric(.Cells(rw, 1)) And IsNumeric(.Cells(rw, 2)) Then
With .Columns(Application.Max(4, .Cells(1, Columns.Count).End(xlToLeft).Column + 1))
.Cells(1, 1) = .Parent.Cells(rw, 1).Value2
.DataSeries Rowcol:=xlColumns, Type:=xlLinear, Date:=xlDay, _
Step:=1, Stop:=.Parent.Cells(rw, 2).Value2
End With
End If
Next rw
End With
End Sub
You could use this:
Sub test()
Dim Lastrow As Long
Dim j As Double, i As Double, r As Double
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet1") ' Change the name of your sheet
Lastrow = ws.Range("A" & Rows.Count).End(xlUp).Row
j = 4 ' Column D
With ws
For i = 1 To Lastrow ' Start the loop at A1 until the last row in column A
.Cells(1, j) = .Cells(i, 1).Value
r = 1
Do
.Cells(r + 1, j) = .Cells(r, j) + 1
r = r + 1
Loop Until .Cells(r, j) = .Cells(i, 2).Value
j = j + 1
Next i
End With
End Sub
Here's another quick one just for fun:
Sub transposeNfill()
Dim lastRow&, i&, xStart$, xEnd$, xMid$
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To lastRow
xStart = Cells(i, 1)
xEnd = Cells(i, 2)
xMid = xEnd - xStart
Cells(1, i + 3).Value = xStart
Cells(1 + xMid, i + 3) = xEnd
Range(Cells(2, i + 3), Cells(xMid, i + 3)).FormulaR1C1 = "=r[-1]c+1"
Cells.Copy
Cells.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Next i
End Sub
Related
I have the issue with stacking in the loop
The macro should combine all columns (changeable number of rows) into one column.
Sub CombineColumns()
Dim xRng As Range
Dim i As Integer
Dim xLastRow As Integer
On Error Resume Next
Set xRng = Application.Range("A1", Range("A1").End(xlDown).End(xlToRight))
xLastRow = xRng.Columns(1).Rows.Count + 1
For i = 2 To xRng.Columns.Count
Range(xRng.Cells(1, i), xRng.Cells(xRng.Columns(i).Rows.Count, i)).Cut
ActiveSheet.Paste Destination:=xRng.Cells(xLastRow, 1)
xLastRow = xLastRow + xRng.Columns(i).Rows.Count
Next
End Sub
Using Array is simple and fast.
Sub test()
Dim Ws As Worksheet, toWS As Worksheet
Dim vDB, vR()
Dim i As Long, j As Integer, n As Long
Set Ws = ActiveSheet
vDB = Ws.UsedRange
r = UBound(vDB, 1)
c = UBound(vDB, 2)
For i = 1 To r
For j = 1 To c
n = n + 1
ReDim Preserve vR(1 To n)
vR(n) = vDB(i, j)
Next j
Next i
Set toWS = Sheets.Add ' set toWs = Sheets(2) ~~> set your sheet
With toWS
.Cells.Clear
.Range("a1").Resize(n) = WorksheetFunction.Transpose(vR)
End With
End Sub
If I got you right you want to do sth. like that
Option Explicit
Sub CombineColumns()
Dim xRng As Range
Dim i As Long
Dim xLastRow As Long
'On Error Resume Next
Set xRng = Application.Range("A1", Range("A1").End(xlToRight))
xLastRow = lastRow(1) + 1
For i = 2 To xRng.Columns.Count
Range(xRng.Cells(1, i), xRng.Cells(lastRow(i), i)).Cut
ActiveSheet.Paste Destination:=xRng.Cells(xLastRow, 1)
xLastRow = lastRow(1) + 1
Next
End Sub
Function lastRow(col As Long, Optional wks As Worksheet) As Long
If wks Is Nothing Then
Set wks = ActiveSheet
End If
lastRow = wks.Cells(wks.Rows.Count, col).End(xlUp).Row
End Function
The code still needs some improvement as it might loop over all columns espeically if there is no data.
This assumes on all your columns you have data on the 2nd row, to correctly identify the last column.
Option Explicit
Public Sub CombineColumns()
Dim LastColumn As Long, LastRow As Long, LastRowA As Long, i As Long, RngAddress As String
With ActiveSheet
' This assumes you have data on row 2 on all columns
LastColumn = .Cells(2, .Columns.Count).End(xlToLeft).Column
For i = 2 To LastColumn
' Get the last row of Col A on each iteration
LastRowA = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
' Get last row of the Col we're checking
LastRow = .Cells(.Rows.Count, i).End(xlUp).Row
' Get the used range address of the current Col
RngAddress = .Range(.Cells(1, i), Cells(LastRow, i)).Address
' Check if we have blank cells among the rows of the current Col
.Range(.Cells(1, i), Cells(LastRow, i)).Value2 = Evaluate("IF(NOT(ISBLANK(" & RngAddress & "))," & RngAddress & ")")
' Compress data (if there's no empty cells in the current Col the below line will give error, that's the role of err handling)
On Error Resume Next
.Range(.Cells(1, i), Cells(LastRow, i)).SpecialCells(xlCellTypeConstants, 4).Delete xlShiftUp
On Error GoTo 0
' Update the last row in case we compressed data
LastRow = .Cells(.Rows.Count, i).End(xlUp).Row
' Paste data in Col A
.Range(.Cells(1, i), Cells(LastRow, i)).Cut Destination:=.Range("A" & LastRowA)
Next i
Application.CutCopyMode = False
End With
End Sub
Maybe this could be a convenient solution for you :
Sub CombineColumns()
Dim LastRow As Long
LastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
Range("A2:A" & LastRow).Formula = "=B2 & C2 & D2 & E2 & F2 & G2 & H2" 'Insert here the columns you need to be combined
End Sub
Let me know if changes are necessary.
I have been googling a lot and I couldnt make this one work in any way.
I have a table of three columns (Name, Value1, Value2) with lots of names.
I need a vba to list all numbers between value 1&2 including them and their respective names.
For example for row A, 3000, 3003 make rows A, 3000; A, 3001; A, 3002; A, 3003 and then continue on the next name and split that name's range into individual numbers.
Is this even possible?
Thank you so much.
I wrote one based on an array to collect then transfer the values.
Sub expandValues()
Dim i As Long, j As Long, arr As Variant
With Worksheets("sheet5")
.Cells(1, "E").Resize(1, 2) = Array("Name", "Value")
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
ReDim arr(.Cells(i, "B").Value2 To .Cells(i, "C").Value2, 1 To 2)
For j = LBound(arr, 1) To UBound(arr, 1)
arr(j, 1) = .Cells(i, "A").Value2
arr(j, 2) = j
Next j
.Cells(.Rows.Count, "E").End(xlUp).Offset(1, 0). _
Resize(UBound(arr, 1) - LBound(arr, 1) + 1, UBound(arr, 2)) = arr
Next i
End With
End Sub
Addendum:
Here is yours with an outer loop to process through the rows.
Sub FillIN()
Dim stri As Long, endi As Long
Dim nm As string, i as long, j as long
with workSheets(1)
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
nm = .Cells(i, "A").Value
strti = .Cells(i, "B").Value
endi = .Cells(i, "C").Value
For j= strti To endi
.Cells(.Rows.Count, "E").End(xlUp).Offset(1, 0) = nm
.Cells(.Rows.Count, "E").End(xlUp).Offset(0, 1) = j
Next j
next i
end with
End Sub
Something like the following?
Option Explicit
Public Sub ListLines()
Dim ws As Worksheet, i As Long, y As Long, rowCounter As Long
Application.ScreenUpdating = False
Set ws = ActiveSheet: rowCounter = 1
With ws
For i = 2 To GetLastRow(ws, 1)
For y = .Cells(i, 2) To .Cells(i, 3)
.Cells(rowCounter, 5) = .Cells(i, 1)
.Cells(rowCounter, 6) = y
rowCounter = rowCounter + 1
Next y
Next i
End With
Application.ScreenUpdating = True
End Sub
Public Function GetLastRow(ByVal ws As Worksheet, ByVal columNum As Long) As Long
With ws
GetLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
End Function
This is what I have so far. And it semi works. I need to make it jump on the next line ( it just repeats the first one forever ) and make it stop when done.
I havent been able to make it add +1 on the row portion of (Row, Column) in starti and endi and Name i after it is done with first row range. Mine also runs indefinitely so I also miss a stop once done.
Sub FillIN()
Dim ws As Worksheet
Dim stri As Long, endi As Long
Dim Name As Variant
Set ws = Sheets(1)
Name = Sheets(1).Cells(2, 1).Value
strti = Sheets(1).Cells(2, 2).Value
endi = Sheets(1).Cells(2, 3).Value
For i = strti To endi
ws.Cells(Rows.Count, 6).End(xlUp).Offset(1, 0).Value = i
ws.Cells(Rows.Count, 5).End(xlUp).Offset(1, 0).Value = Name
Next i
End Sub
I have two similar macros I've written, and for efficiency's sake I'd like to consolidate them into one. The first macro adds 4 blank rows on another tab underneath a specific row, where column C matches certain criteria. The second macro copies 4 rows of data from an existing tab over to the new tab, and pastes that data into the 4 newly created blank rows. Any help would be greatly appreciated! Thank you
Conceptual screenshots attached:
Screenshot 1: Initial State
Screenshot 2: MACRO 1 inserts 4 rows if criteria in column C is met (in this case value = "Part A"
Screenshot 3: MACRO 2 pulls in row data from another sheet and pastes it into the new blank rows on this sheet
FIRST MACRO:
Sub RowAdder_01()
Dim Col As Variant
Dim BlankRows As Long
Dim LastRow As Long
Dim R As Long
Dim StartRow As Long
Dim PN_01 As Range
Set PN_01 = Range("M17")
Col = "C"
StartRow = 1
BlankRows = 1
LastRow = Cells(Rows.Count, Col).End(xlUp).Row
Application.ScreenUpdating = False
With Worksheets("NEW SHEET")
For R = LastRow To StartRow + 1 Step -1
If .Cells(R, Col) = PN_01 Then
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
SECOND MACRO:
Sub PasteRowData_01()
Dim Col As Variant
Dim BlankRows As Long
Dim LastRow As Long
Dim R As Long
Dim StartRow As Long
Dim PN_01 As Range
Set PN_01 = Range("M17")
Col = "C"
Drop = "A"
StartRow = 1
BlankRows = 1
LastRow = Cells(Rows.Count, Col).End(xlUp).Row
Application.ScreenUpdating = False
Sheets("OLD SHEET").Rows("54:57").SpecialCells(xlCellTypeVisible).Select
Selection.Copy
With Worksheets("NEW SHEET")
For R = LastRow To StartRow + 1 Step -1
If .Cells(R, Col) = PN_01 Then
Sheets(NEW SHEET).Select
.Cells(R + 1, Drop).Select
Selection.PasteSpecial
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
Please try this code.
Option Explicit
Sub AddAndPaste()
Dim Ws As Worksheet
Dim Arr As Variant
Dim PN_01 As Variant
Dim Last As Long ' column or row
Dim R As Long
' copy from source
Set Ws = Worksheets("Old Sheet")
With Ws
With .UsedRange
Last = .Columns.Count + .Column - 1
End With
Arr = Range(.Cells(54, 1), .Cells(57, Last)).SpecialCells(xlCellTypeVisible).Value
End With
Application.ScreenUpdating = False
' paste to destination
Set Ws = Worksheets("New Sheet")
With Ws
PN_01 = .Cells(7, "M").Value
Last = .Cells(.Rows.Count, "C").End(xlUp).Row
For R = Last To 1 Step -1
If .Cells(R, "C").Value = PN_01 Then
With .Cells(R, "A")
.Resize(4, 1).EntireRow.Insert Shift:=xlDown
.Offset(-4).Resize(UBound(Arr, 1), UBound(Arr, 2)).Value = Arr
End With
Exit For ' don't exit if you need to continue looping
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
Your problem is caused by inserting a line. We recommend using an array.
Sub test()
Dim Ws As Worksheet, newWs As Worksheet, Temp As Worksheet
Dim vDB, vSp, vR()
Dim i As Long, r As Long, n As Long, k As Integer, cnt As Integer
Dim PN_01 As Range
Set newWs = Sheets("New Sheet")
Set oldWs = Sheets("OLD SHEET")
Set Temp = Sheets.Add
oldWs.Range("a54:d57").SpecialCells(xlCellTypeVisible).Copy Temp.Range("a1")
vSp = Temp.UsedRange
Application.DisplayAlerts = False
Temp.Delete
Application.DisplayAlerts = True
With newWs
vDB = .Range("a1", "d" & .Range("a" & Rows.Count).End(xlUp).Row)
Set PN_01 = .Range("M17")
End With
cnt = UBound(vSp, 1)
r = UBound(vDB, 1)
For i = 1 To r
n = n + 1
ReDim Preserve vR(1 To 4, 1 To n)
For j = 1 To 4
vR(j, n) = vDB(i, j)
Next j
If vDB(i, 3) = PN_01 Then
For k = 1 To cnt
n = n + 1
ReDim Preserve vR(1 To 4, 1 To n)
For j = 1 To 4
vR(j, n) = vSp(k, j)
Next j
Next k
End If
Next i
newWs.Range("a1").Resize(n, 4) = WorksheetFunction.Transpose(vR)
newWs.Activate
End Sub
i am having problem with the task i am doing right now. Supposedly,i have a for loop to calculate the MIN and SUM of the used range (tables). Since i have 10 worksheet in the workbook, i added another for loop to loop on each worksheet in the workbook. However, the codes run without error but the output is not as expected. It will calculate even at the unused range. Is there any error on my code?
Sub calc()
Dim ws As Worksheet
Dim y As Workbook
Dim rng As Range
Dim i As Integer, r As Long, j As Long
Set y = ThisWorkbook
For Each ws In y.Worksheets
For Each rng In ws.UsedRange.SpecialCells(xlCellTypeConstants, 3).Areas
If rng.Rows.Count > 1 And rng.Columns.Count = 14 Then
j = 2
r = rng.Cells(rng.Rows.Count, 1).Row + 1
Cells(r, rng.Columns(1).Column).Value = "SUMMARY"
For i = rng.Columns(2).Column To rng.Columns(2).Column + 12
If i = rng.Columns(12).Column Then
Cells(r, i).Formula = "=MIN(" & rng.Columns(j).Address & ")"
j = j + 1
Else
Cells(r, i).Formula = "=SUM(" & rng.Columns(j).Address & ")"
j = j + 1
End If
Next i
End If
Next rng
Next
End Sub
You should always qualify which sheet you are referring to when you use Cells, Range, etc.
For instance, the line
Cells(r, rng.Columns(1).Column).Value = "SUMMARY"
is referring to the active sheet, but you probably want to refer to the sheet that ws is referring to, i.e.
ws.Cells(r, rng.Columns(1).Column).Value = "SUMMARY"
Your code should probably look like:
Sub calc()
Dim ws As Worksheet
Dim y As Workbook
Dim rng As Range
Dim i As Integer, r As Long, j As Long
Set y = ThisWorkbook
For Each ws In y.Worksheets
For Each rng In ws.UsedRange.SpecialCells(xlCellTypeConstants, 3).Areas
If rng.Rows.Count > 1 And rng.Columns.Count = 14 Then
j = 2
r = rng.Cells(rng.Rows.Count, 1).Row + 1
ws.Cells(r, rng.Columns(1).Column).Value = "SUMMARY"
For i = rng.Columns(2).Column To rng.Columns(2).Column + 12
If i = rng.Columns(12).Column Then
ws.Cells(r, i).Formula = "=MIN(" & rng.Columns(j).Address & ")"
j = j + 1
Else
ws.Cells(r, i).Formula = "=SUM(" & rng.Columns(j).Address & ")"
j = j + 1
End If
Next i
End If
Next rng
Next
End Sub
I wanna write a macro which copy the 1 cells to another sheet if they contain some value.
Table:
Expectation:
So far I tried this but it copy only last cell from sheet1 to first cell in sheet 2
Sub CopyBasedonSheet1()
Dim i As Integer
Dim j As Integer
Sheet1LastRow = Worksheets("Sheet1").Range("B" & Rows.Count).End(xlUp).Row
Sheet2LastRow = Worksheets("Sheet2").Range("B" & Rows.Count).End(xlUp).Row
For j = 1 To Sheet1LastRow
For i = 1 To Sheet2LastRow
If Worksheets("Sheet1").Cells(j, 2).Value = "a" Then
Worksheets("Sheet2").Cells(i, 1).Value = Worksheets("Sheet1").Cells(j, 1).Value
Else
End If
Next i
Next j
End Sub
You should do it with one loop, because when you have a row from the first sheet, there is only 1 place where you want to copy it, not many:
Sub CopyBasedonSheet1()
Dim i As Integer
Dim j As Integer
Sheet1LastRow = Worksheets("Sheet1").Range("B" & Rows.Count).End(xlUp).Row
i = 1
For j = 1 To Sheet1LastRow
If Worksheets("Sheet1").Cells(j, 2).Value = "a" Then
Worksheets("Sheet2").Cells(i, 1).Value = Worksheets("Sheet1").Cells(j, 1).Value
Worksheets("Sheet2").Cells(i, 2).Value = Worksheets("Sheet1").Cells(j, 2).Value
i = i + 1
End If
Next j
End Sub
Or you may try a different approach altogether which is faster also...
Sub CopyData()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim x, y()
Dim i As Long, j As Long
Set ws1 = Sheets("Sheet1")
Set ws2 = Sheets("Sheet2")
x = ws1.Range("A1").CurrentRegion.Value
ReDim y(1 To Application.CountIf(ws1.Columns(2), "a"), 1 To 2)
j = 1
For i = 1 To UBound(x, 1)
If x(i, 2) = "a" Then
y(j, 1) = x(i, 1)
y(j, 2) = x(i, 2)
j = j + 1
End If
Next i
ws2.Range("A:B").Clear
ws2.Range("A1").Resize(UBound(y, 1), 2).Value = y
End Sub