Paste values using range function in VBA - excel

I'm using this code to paste values for a range but I started facing issues whenever my data is in a million or more line numbers, I wanted to break the range and run the same code in 4/5 parts (loops), can some one help me with it
Range("F14:J14").Select
Selection.Copy
With ActiveSheet
RowCount = .Cells(.Rows.Count, "B").End(xlUp).Row
End With
Range("F14:J14").Select
Selection.Copy
Range("f15:J" & RowCount).Select
Selection.PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("f15:J" & RowCount).Select
Selection.Copy
Range("f15:J" & RowCount).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False

You are right, copying very large number of cells is problematic, so processing the data in blocks is a good idea.
That said, relying on Select and Copy PasteSpecial is also problematic.
I would suggest this alternative
Sub Demo()
Dim rSrc As Range
Dim rDst As Range
Dim rBlk As Range
Dim RowCount As Long
Dim CopyRowStart As Long
Dim CopyRowNum As Long
' Set number of rows to process at a time
CopyRowNum = 100000
' Set references to source and Destination ranges
With ActiveSheet
Set rSrc = .Range("F14:J14")
RowCount = .Cells(.Rows.Count, "B").End(xlUp).Row
Set rDst = .Range("F15:J" & RowCount)
End With
' Copy data in blocks
CopyRowStart = 0
Set rBlk = rDst.Resize(CopyRowNum)
Do While CopyRowStart + CopyRowNum <= rDst.Rows.Count
' Copy formulas
rBlk.Formula = rSrc.Formula
' Convert to values
rBlk.Value = rBlk.Value
' Move to next block
If rBlk.Row + CopyRowNum + CopyRowStart - 1 > rDst.Row + rDst.Rows.Count - 1 Then
Exit Do
End If
Set rBlk = rBlk.Offset(CopyRowNum, 0)
CopyRowStart = CopyRowStart + CopyRowNum
DoEvents
Loop
' Copy remaining rows
If rBlk.Row + CopyRowNum <= rDst.Row + rDst.Rows.Count - 1 Then
Set rBlk = rBlk.Resize(rDst.Row + rDst.Rows.Count - rBlk.Row - CopyRowNum)
Set rBlk = rBlk.Offset(CopyRowNum, 0)
rBlk.Formula = rSrc.Formula
rBlk.Value = rBlk.Value
End If
End Sub
Note, the rather convoluted range size calculation are designed to avoid exceeding the size of the sheet, when the number of rows nears the end of the sheet (1,048,576 rows)

Related

How to work with the range method with only one specific cell?

I would like the cells I have selected in the spreadsheet to receive the +1 increment. The code below works fine when I have a range, but when I have only one cells selected the code adds +1 to every cell in the spreadsheet.
Sub Macro_MAIS_1()
'
' Macro_MAIS_1 Macro
'
'
Dim AlocationWorksheet As Worksheet
Dim ActSheet As Worksheet
Dim SelRange As Range
Dim iCells As Integer
On Error GoTo Fim
Set AlocationWorksheet = Worksheets("ALOCAÇÃO")
AlocationWorksheet.Unprotect
Set ActSheet = ActiveSheet
Set SelRange = Selection.SpecialCells(xlCellTypeVisible)
iCells = SelRange.Cells.Count
Range("O7").Select
Selection.Copy
SelRange.PasteSpecial Paste:=xlPasteValues, Operation:=xlAdd, SkipBlanks _
:=False, Transpose:=False
Exit Sub
Fim:
MsgBox Selection.Address
Range("O7").Select
Selection.Copy
SelRange.PasteSpecial Paste:=xlPasteValues, Operation:=xlAdd, SkipBlanks _
:=False, Transpose:=False
End Sub
I would avoid using a selection, but this should work. If you have text you'll run into trouble and need to write out some checks. You also should not be counting all cells, as you might have an overflow of values. Check rows and columns, but not both.
Sub addPlusOne()
Dim aRange As Range, i As Long, j As Long
Set aRange = Selection
If aRange.Rows.Count > 1 Or aRange.Columns.Count > 1 Then
Dim zRng()
zRng = aRange.Value
For i = LBound(zRng) To UBound(zRng)
For j = LBound(zRng, 2) To UBound(zRng, 2)
zRng(i, j) = zRng(i, j) + 1
Next j
Next i
aRange.Value = zRng
Else
aRange.Value = aRange.Value + 1
End If
End Sub
EDIT: OP commented that they want to use visible selection. While this isn't best practice, this will work.
Sub plusOneOnSelection()
Dim aCell As Range
For Each aCell In Selection.SpecialCells(xlCellTypeVisible).Cells
If IsNumeric(aCell) Then aCell.Value = aCell.Value + 1
Next aCell
End Sub

Macro to copy and paste (transpose) data from column to row - Scalable

I am looking to create a macro which would allow me to copy and paste data from one column and then transpose that data over 2 columns in the right order
I have recorded a macro while doing the process manually
Range("G3").Select
Application.CutCopyMode = False
Selection.Copy
Range("G2:G7").Select ' (The column range I want to copy)
Application.CutCopyMode = False
Selection.Copy
Range("I1").Select ' (Row where the range of G2:G7) is now transposed)
Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=True
Range("H2:H7").Select ' (The second column range I want to copy)
Application.CutCopyMode = False
Selection.Copy
Range("I2").Select ' (Second Row where the range of H2:H7) is now transposed)
Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=True
Range("H8:H13").Select ' (The third column range I want to copy)
Application.CutCopyMode = FalseSelection.Copy
Range("I3").Select' ( Third Row where the range of H8:H13) is now transposed)
Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=True
The problem is that this code only works up to certain number of rows (up till H13 for example), but if I want to this repeat this process up to row H600 (range of H600:H605) and pasting to I31 for example without copying and pasting this code hundreds of times, is there a way I can do this?
This is what I mean by example
Column H
Star
Greenwood
Titon
Humford
converted to
Column I | Column J**
Star | Greenwood
titon | Humford
Here's an alternative to Copy/Paste - using Variant Arrays. This will be much faster for large data sets.
Sub Demo()
Dim rng As Range
Dim Src As Variant
Dim Dst As Variant
Dim GroupSize As Long
Dim Groups As Long
Dim iRow As Long
Dim iCol As Long
Dim iDst As Long
Dim SrcStartRow As Long
Dim SrcColumn As Long
Dim DstStartRow As Long
Dim DstColumn As Long
' Set up Parameters
GroupSize = 2
SrcStartRow = 2
SrcColumn = 8 'H
DstStartRow = 1
DstColumn = 9 'I
With ActiveSheet 'or specify a specific sheet
' Get Reference to source data
Set rng = .Range(.Cells(SrcStartRow, SrcColumn), .Cells(.Rows.Count, SrcColumn).End(xlUp))
' Account for possibility there is uneven amount of data
Groups = Application.RoundUp(rng.Rows.Count / GroupSize, 0)
If rng.Rows.Count <> Groups * GroupSize Then
Set rng = rng.Resize(Groups * GroupSize, 1)
End If
'Copy data to Variant Array
Src = rng.Value2
'Size the Destination Array
ReDim Dst(1 To UBound(Src, 1) / GroupSize, 1 To GroupSize)
'Loop the Source data and split into Destination Array
iDst = 0
For iRow = 1 To UBound(Src, 1) Step GroupSize
iDst = iDst + 1
For iCol = 1 To GroupSize
Dst(iDst, iCol) = Src(iRow + iCol - 1, 1)
Next
Next
' Move result to sheet
.Cells(DstStartRow, DstColumn).Resize(UBound(Dst, 1), UBound(Dst, 2)).Value = Dst
End With
End Sub
Before
Well, you are not really transposing, but I would use this method. I start at 2 to leave the first in place, then basically move the next one over and delete all the empty spaces at the end.
Sub MakeTwoColumns()
Dim x As Long
For x = 2 To 500 Step 2
Cells(x, 6) = Cells(x, 5)
Cells(x, 5).ClearContents
Next x
Columns(5).SpecialCells(xlCellTypeBlanks).Delete
Columns(6).SpecialCells(xlCellTypeBlanks).Delete
End Sub
After

Incrementing a range

I have 135 rows of data in columns A to U
I am trying to write a script that will help me copy each column of data one under another to a clean worksheet.
Right now i wrote some code that will do it for the first two columns and i would prefer to have it done more automatically/dynamically instead of me copy pasting these two code blocks and altering the ranges
Range("A764:A897").Select
Selection.Copy
Sheets("New").Select
Range("A1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone,
SkipBlanks _
:=False, Transpose:=False
Sheets("Rom").Select
Range("B764:B897").Select 'id like to have this increment automaticaly
Selection.Copy
Sheets("New").Select
Range("A135").Select 'id like to have this increment automaticaly
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone,
SkipBlanks _
:=False, Transpose:=False
Try this. Adjust sheet name as necessary.
You can speed up the operation by directly transferring values rather then copying and pasting.
You could define the 134 as a constant so you only have to change once in the code rather than three times.
Sub x()
Dim rCopy As Range
Dim r As Long: r = 1
Set rCopy = Sheets("Name of source sheet").Range("A764").Resize(134) 'adjust sheet name
Do Until IsEmpty(rCopy(1))
Sheets("New").Cells(r, 1).Resize(134).Value = rCopy.Value
Set rCopy = rCopy.Offset(, 1)
r = r + 134
Loop
End Sub
Supposing your data in sheet “Rom” start at row 764:
Sub test()
Dim ws1, ws2 as string
Dim i, lr, lc as long
ws1 = “Rom”
ws2 = “New”
lc = sheets(ws1).cells(764,columns.count).end(xltoleft).column
For i = 1 to lc
lr = sheets(ws2).cells(Rows.count,1).End(xlUp).row + 1
sheets(ws1).range(cells(i, 764),cells(i,897)).Select
Selection.Copy
Sheets(ws2).cells(lr,1).Select
Selection.PasteSpecial Paste:=xlPasteValues
Next
End sub
You can read in each column of data to an array and then paste it into your new column. In this way, you can perform any mutations needed on the data.
If you have 135 rows (always)
Dim ws As Worksheet, arr As Variant, myRange As Range, i As Integer, col As Integer, k As Integer
Set ws = ThisWorkbook.Sheets("Sheet1") ' or whatever your worksheet is
ReDim arr(1 To 135*22) ' 22 letters from A To U
k = 1
With ws
For col = 1 To 22
For i = 764 To 897
arr(k) = .Cells(col, i).Value2 ' if you need to do anything else here
k = k+1
Next i
Next col
End with
Set ws = ThisWorkbook.Sheets("New") 'or wherever this is going
With ws
.Range("A1").Resize(UBound(arr), 1).Value = Application.Transpose(arr)
End with

Copying & Pasting

Objective: I'm trying to copy, find and paste data as per the find (that is Region).
Problem: I'm getting the desired output when I'm defining where to paste the data. But this is not what the macro is suppose to do. It's suppose to look for that Region name and then paste the data under the appropriate title and so on.
Here is what I've written so far:
Sub DataPasting()
ApplicationUpdating = False
Sheets("Sheet1").Range("I2:J2").Copy 'copy and pasting the data set from Sheet1
Sheets("Stories & Topics").Select
Dim RegionColumn As Long
Dim erow As String
RegionColumn = Application.WorksheetFunction.Match(Sheets("Raw").Range("H1"), Sheets("Stories & Topics").Range("A1:Z1"), False)
erow = ThisWorkbook.Worksheets("Stories & Topics").Cells(Rows.Count, "B").End(xlUp).Row
ThisWorkbook.Worksheets("Stories & Topics").Paste (ThisWorkbook.Worksheets("Stories & Topics").Range("B" & erow + 1))
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
ApplicationUpdating = True
End Sub
Note:
Sheet1 = Sheet from where the data is to be copied
Stories & Topics = Destination sheet where the data has to be pasted
I also tried Vlookup and Match but no use.
Thanks!
It's difficult to tell from your code exactly what you're doing but something like this should work:
Sub DataPasting()
Dim RegionColumn 'variant
Dim erow As Long
Dim shtRaw As Worksheet, shtSaT As Worksheet, shtOne As Worksheet
Set shtRaw = ThisWorkbook.Sheets("Raw")
Set shtSaT = ThisWorkbook.Sheets("Stories & Topics")
Set shtOne = ThisWorkbook.Sheets("Sheet1")
ApplicationUpdating = False
RegionColumn = Application.Match(shtRaw.Range("H1").Value, _
shtSaT.Range("A1:Z1"), 0)
If Not IsError(RegionColumn) Then
erow = shtSaT.Cells(Rows.Count, "B").End(xlUp).Row
shtSaT.Cells(erow, RegionColumn).Resize(1, 2).Value = shtOne.Range("I2:J2").Value
End If
ApplicationUpdating = True
End Sub

how do "relocate" cell values in a single column to a single row using Offset?

I am a bad VBA person. Please help me.
I want to relocate three values in a single column and put them in a single row using Offset. I need to flatten 3 rows of data into a single row of data.
Here is the code - it's very crude:
Sub Macro1()
'
' Macro1 Macro
'
'turn off display update
Application.ScreenUpdating = False
Dim CVFESUMMARY2(2000, 2000)
Dim MAXROW As Integer
Dim i As Integer
Dim r As Range
Dim x As Range
Dim y As Range
Dim z As Range
Set r = Range("BJ13:BJ512")
Set x = Range("BK13:BK512")
Set y = Range("BL13:BL512")
Set z = Range("BM13:BM512")
MAXROW = 300
'format "new" columns
Range("BK11").Select
ActiveCell.FormulaR1C1 = "NORM"
Range("BL11").Select
ActiveCell.FormulaR1C1 = "MIN"
Range("BM11").Select
ActiveCell.FormulaR1C1 = "MAX"
Columns("BJ:BM").Select
Selection.ColumnWidth = 12
'define the "COPY DATA FROM" starting cell location
Sheets("CVFESUMMARY2").Select
Range("BJ13").Select
'cycle through all of the rows in range r
For i = 1 To MAXROW
'copy "BJ13"
r.Select
Selection.Copy
'paste "value only" in column "BK13"
x.Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
'copy "BJ13+1"
Set r = r.Offset(1, 0)
r.Select
Selection.Copy
'paste "value only" in column "BL13"
y.Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
'copy "BJ13+2"
Set r = r.Offset(1, 0)
r.Select
Selection.Copy
'paste "value only" in column "BM13"
z.Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
'move active cell to "BJ13+4"
Set r = r.Offset(2, 0)
Set x = x.Offset(4, 0)
Set y = y.Offset(4, 0)
Set z = z.Offset(4, 0)
Next i
'turn on display update
Application.ScreenUpdating = True
End Sub
This somewhat works but it is adding values in rows +2 and +3 that I don't want; I think the looping is wrong. Thanks in advance!
Before
After
Your desired output, can the results be compacted? (all empty rows removed, leaving a block of data) or is there information in the columns before that its linked with?
Removing the extra rows wouldn't be much extra work.
With the following code (which I think does what you want) the MaxRows value is incorrect. The way it works this should be a MaxRecords ie: the number of groups of data you.
Sub Transpose()
Dim Position As Range
Dim Source As Range
Dim MaxRow As Integer
Dim Index As Integer
' set column titles
Range("BK11").Value2 = "NORM"
Range("BL11").Value2 = "MIN"
Range("BM11").Value2 = "MAX"
' set the width
Range("BJ:BM").ColumnWidth = 12
MaxRow = 512 ' see note below
Set Position = Range("BJ13") ' define the start position
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'For Index = 1 To MaxRow
Do
' create a range that contains your first 3 values
Set Source = Range(Position, Position.Offset(RowOffset:=2))
' copy it
Source.Copy
' paste and transpose the values into the offset position
Position.Offset(ColumnOffset:=1).PasteSpecial xlPasteValues, SkipBlanks:=False, Transpose:=True
' OPTIONAL - Clear the contents of your source range
Source.ClearContents
' re-set the position ready for the next iteration
Set Position = Position.Offset(RowOffset:=4)
'Next
Loop While Position.Row < RowMax
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Note: I've not used Select and Selection as they confuse me! Using Range() makes it simpler to know where you are imo.
Update I've included one that also compacts the output
Sub TransposeCompact()
Dim Position As Range
Dim Source As Range
Dim Destination As Range
Dim MaxRow As Integer
Dim Index As Integer
' set column titles
Range("BK11").Value2 = "NORM"
Range("BL11").Value2 = "MIN"
Range("BM11").Value2 = "MAX"
' set the width
Range("BJ:BM").ColumnWidth = 12
MaxRow = 512 ' see note below
' define the start position
Set Position = Range("BJ13")
' define the first output position
Set Destination = Position.Offset(ColumnOffset:=1)
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'For Index = 1 To MaxRow
Do
' create a range that contains your first 3 values
Set Source = Range(Position, Position.Offset(RowOffset:=2))
' copy it
Source.Copy
' paste and transpose the values into the offset position
Destination.PasteSpecial xlPasteValues, SkipBlanks:=False, Transpose:=True
' OPTIONAL - Clear the contents of your source range
Source.ClearContents
' re-set the position ready for the next iteration
Set Position = Position.Offset(RowOffset:=4)
' increment the row on the output for the next iteration
Set Destination = Destination.Offset(RowOffset:=1)
'Next
Loop While Position.Row < RowMax
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Update 2
Your i variable used in the For Loop is not actually used, if your data is in rows 13 to 512 then the edits I've made to the code above should help.
The RowMax variable now will stop the macro when Position.Row goes beyond it.

Resources