Generate 1:N Sequence Array - excel

I am currently trying to generate an array of sequential numbers (1:N) in order to populate a horizontal range ($C$6:N). When I use application.transpose my entire range is populated with 1, and when I don't use it the entire range is blank. I have attached my code below. pn is the range I want to populate and nop is the count of it. Thank you!
Best,
M
pn.Value = Array(Application.WorksheetFunction.Sequence(1, nop.Value))

I've put 2 options below,
Number one: (this seems to be what you want)
Sub generateSequence_MethodOne()
'Start at a cell and generate till a number
Dim pn As Range
Dim nop As Long
Set pn = Range("C6") 'starting cell
nop = 250 'number of entries
With pn
.value = 1
.AutoFill Destination:=pn.Resize(, nop), Type:=xlFillSeries
End With
' 'if you want to loop instead, then use this instead of the above
' For i = 1 To nop
' pn.Offset(, i - 1).value = i
' Next
End Sub
and number two, use this if you have known range that you want to fill, not knowing until what number
Sub generateSequence_MethodTwo()
'set a range and fill it with a sequence
Dim cell As Range
Dim n As Long
n = 1
For Each cell In Range("C6:Z6").Cells 'known range to fill
cell.value = n
n = n + 1
Next
End Sub

To use SEQUENCE() within VBA to fill from B9 to M9:
Sub FillUsingSequence()
Dim rng As Range
Set rng = Range("B9:M9")
rng.Value = Application.WorksheetFunction.Sequence(1, rng.Count, 1, 1)
End Sub

Related

Copying Cell Value On The Same Row Based On Another Column Cell Color

I am trying to be able to search through a list in Column W and any cell in Column W that is highlighted Yellow to Copy Cell from Column B from the same row as colored cell in Column W.
This is what I have so far:
Sub CopyData()
Dim YellowField As Range
Dim YellowCell As Range
Dim Amortized As Worksheet
Dim Rollforward As Worksheet
Set Amortized = Worksheets("AMORTIZED")
Set Rollforward = Worksheets("Rollforward")
Set YellowField = Amortized.Range("W4", Amortized.Range("W4").End(xlDown))
For i = 4 To YellowField.UsedRange.Rows.Count
For Each YellowCell In YellowField
If YellowCell.Interior.Color = vbYellow Then
x = Amortized.Cells(i, ColumnD).Value
x.Copy Destination:= _
Rollforward.Range("B30").Offset(Rollforward.Rows.Count - 1, 0).End(xlUp).Offset(1, 0)
Exit For
End If
Next YellowCell
Exit For
Next i
End Sub
Currently when I run I get Error 439 on this line.
x = Amortized.Cells(i, ColumnD).Value
I was using .color and switched to .value because I feel it would assume if column D was colored I could be wrong though. Plus I feel I am still missing the loop here where the list would continue to be scanned for more colored cells in Column W
ColumnD is being read as a Variant it does not mean Column D Use 4 instead.
The inner loop is not needed and will give you a lot of false positive copies.
And to copy X you need to make it a Range and Set it. But in this case just use the cell itself.
Sub CopyData()
Dim LstRow As Long
Dim Amortized As Worksheet
Dim Rollforward As Worksheet
Set Amortized = Worksheets("AMORTIZED")
Set Rollforward = Worksheets("Rollforward")
LstRow = Amortized.Cells(Amortized.Rows.Count,23).End(xlUp).Row
For i = 4 To lstrow
If Amortized.Cells(i,23).Interior.Color = vbYellow Then
Amortized.Cells(i, 4).Copy Destination:= _
Rollforward.Cells(Rollforward.Rows.Count,2).End(xlUp).Offset(1,0)
End If
Next i
End Sub

Copy values from cells in range and paste them in random cell in range

I have two ranges as showed in this picture.
I'm trying to write a VBA macro that successively selects a single cell in the first range (“B23, F27”) , copies the selected cell's value, then selects a random cell in the second range (“G23, K27”), and pastes the first cell's value into the randomly selected cell in the second range.
This should repeat until every cell from the first range has been copied, or every cell in the second range is filled with a new value. In this example both outcomes are equivalent as both ranges have the same number of cells (25).
The result should be like the second image.
I tried to assign the first range to an array and then pick a random value from this array and paste it to the second range.
I also tried to extract unique values from the first range, build a dictionary with it then pick a random cell from the second range and a random value from the dictionary and paste it.
Later I tried again using the VBA syntax “with range” and f"or each cell in range" but I can’t just come up with something that actually works. Sometimes the second range is filled by various values, but not as intended.
First example: this one just does not work
Sub fillrange()
Dim empty As Boolean
'This part checks if every cell in the first range as a value in it
For Each Cell In Range("B23", "F27")
If Cell.Value = "" Then
empty = True
End If
Next
'If every cell is filled then
If empty Then
Exit Sub
Else:
With ThisWorkbook.Worksheets("Sheet1)").Range("B23", "F27")
.Cells(Application.WorksheetFunction.RandBetween(1, 25)).Select
.Copy 'the cell select works, but it will copy all range
'This does not work
'For Each Cell In Range("G23", "K27")
'Cells(Application.WorksheetFunction.RandBetween(1, 25)).Select
'.PasteSpecial Paste:=xlPasteValues
'Next
End With
End If
End Sub
Second example: it fills the range but with wrong values
Sub fillrange2()
Dim empty As Boolean
For Each cell In Range("B23", "F27")
If cell.Value = "" Then
empty = True
'This part checks if every cell in the first range as a value in it
Exit For
End If
Next cell
If empty Then
Exit Sub
Else:
Dim ws As Worksheet
Dim lRow As Long, i As Long
Dim col As New Collection, itm As Variant
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
For i = 1 To lRow
On Error Resume Next
col.Add .Range("B23", "F27").Value, CStr(.Range("A" & i).Value)
On Error GoTo 0
Next i
End With
Dim MyAr() As Variant
ReDim MyAr(0 To (col.Count - 1))
For i = 1 To col.Count
MyAr(i - 1) = col.Item(i)
Next
For Each cell In Range("G23", "K27")
cell.Value = Application.WorksheetFunction.RandBetween(LBound(MyAr), UBound(MyAr))
Next
End If
End Sub
Third example: as the second example, it fills the range but with wrong values
Sub fillrange3()
Dim MyAr() As Variant
MyAr = Range("B23", "F27")
For Each cell In Range("G23", "K27")
cell.Value = Application.WorksheetFunction.RandBetween(LBound(MyAr), UBound(MyAr))
Next
End Sub
Maybe something like this ?
Sub test()
Set Rng = Range("G23:K27")
n = 1
totCell = 25
Set oFill = Range("G23")
Set oSource = Range("B23")
For i = 1 To 5
oFill.Value = "X" & n
oFill.AutoFill Destination:=Range(oFill, oFill.Offset(4, 0)), Type:=xlFillSeries
Set oFill = oFill.Offset(0, 1)
n = n + 5
Next i
For i = 1 To 5
Do
RndVal = Int((totCell - 1 + 1) * Rnd + 1)
xVal = "X" & RndVal
Set C = Rng.Find(xVal, lookat:=xlWhole)
If Not C Is Nothing Then
C.Value = oSource.Value
Set oSource = oSource.Offset(1, 0)
check = check + 1
If check = 5 Then Exit Do
End If
Loop
Set oSource = oSource.Offset(-5, 1)
check = 0
Next i
End Sub
I cheat by making a preparation for the range G23 to K27 fill with X1 to X25 in the first for i = 1 to 5.
The second for i = 1 to 5 is to offset from column B to G.
The Do - Loop is to generate random number between 1 to 25.
If the generated number is found then the found cell has the value from the "source",
if not found, it loop until the generated number is found 5 times (hence also the found cell is fill with 5 different source). Then before the next i, the "source" cell is offset to the next column.
This if I'm not wrong to get what you mean.
Here's another approach, just for a bit of variety.
Sub x()
Dim r1 As Range, r2 As Range, i As Long
Dim r As Long, c As Long
Set r1 = Range("B23").Resize(5, 5) 'define our two ranges
Set r2 = Range("G23").Resize(5, 5)
r2.ClearContents 'clear output range
With WorksheetFunction
Do Until .Count(r2) = r2.Count 'loop until output range filled
r = .RandBetween(1, 25) 'random output cell number
If .CountIf(r2, r1.Cells(i)) = 0 Then 'if not in output range already
If r2.Cells(r) = vbNullString Then 'if random cell empty
r2.Cells(r).Value = r1.Cells(i).Value 'transfer value
i = i + 1
End If
End If
Loop
End With
End Sub

Non-contiguous For Each loop per row instead of column

I have a non-contiguous selection spanning rows and columns, and I want to do a For Each loop on it. Excel VBA does this by looping firstly down column 1, then 2,3 etc.; but I want it to loop along the row first instead.
(My sheet looks something like the picture below, I need to loop down the selection (version) each column in turn, and retrieve the Doc. No. and other information. The number of rows and version columns in the sheet is not fixed).
Short of writing a fairly large Sort function and creating an array of references, I was wondering if there was a 'built-in' way to do this?
I don't need code, just an explanation.
The order in which a For Each iterates an object collection is implementation-dependent (IOW blame Excel, not VBA) and, while likely deterministic & predictable, there is nothing in its specification that guarantees a specific iteration order. So VBA code written to iterate an object collection, should not be written with the assumption of a specific iteration order, since that's something that can very well change between versions of the type library involved (here Excel's).
It's very unclear what the shape of your Range / Selection is, but if you need to iterate the selected cells in a specific order, then a For Each loop should not be used, at least not for iterating the cells per se.
Since the ranges are not contiguous, the Range will have multiple Areas; you'll want to iterate the Selection.Areas, and for each selected area, iterate the cells in a particular order. For Each is, by far, the most efficient way to iterate an object collection, which Range.Areas is.
Debug.Assert TypeOf Selection Is Excel.Range
Dim currentArea As Range
For Each currentArea In Selection.Areas
'todo
Next
Instead of nesting the loops, make a separate procedure that takes the currentArea as a parameter - that procedure is where you'll be iterating the individual cells:
Private Sub ProcessContiguousArea(ByVal area As Range)
Dim currentRow As Long
For currentRow = 1 To area.Rows.Count
Debug.Print area.Cells(currentRow, 1).Address
Next
End Sub
Now the outer loop looks like this:
Debug.Assert TypeOf Selection Is Excel.Range
Dim currentArea As Range
For Each currentArea In Selection.Areas
ProcessContiguousArea currentArea
Next
The ProcessContiguousArea procedure is free to do whatever it needs to do with a given contiguous area, using a For loop to iterate the range by rows, without needing to care for the actual address of the selected area: using Range.Cells(RowIndex, ColumnIndex), row 1 / column 1 represents the top-left cell of that range, regardless of where that range is located in the worksheet.
Non-selected cells can be accessed with Range.Offset:
Debug.Print area.Cells(currentRow, 1).Offset(ColumnOffset:=10).Address
The top-left cell's row of the area on the worksheet is returned by area.Row, and the top-left cell's column of the area on the worksheet is retrieved with area.Column.
Non-Contiguous
By looping through the rows first (i), you will get the 'By Row sequence' e.g. A1,B1,C1, ...
The Code
Sub NonContiguous()
Dim i As Long
Dim j As Long
Dim k As Long
With Selection
For k = 1 To .Areas.Count
With .Areas(k)
For i = .Row To .Rows.Count + .Row - 1
For j = .Column To .Columns.Count + .Column - 1
Debug.Print .Parent.Cells(i, j).Address & " = " _
& .Parent.Cells(i, j)
Next
Next
End With
Next
End With
End Sub
This is based on urdearboy's suggestion:
1. loop over columns
2. within a column, loop over cells
Sub disjoint()
Dim r As Range, rInt As Range
Dim nLastColumn As Long
Dim nFirstColumn As Long, msg As String
Dim N As Long
Set r = Range("C3,C9,E6,E13,E15,G1,G2,G3,G4")
nFirstColumn = Columns.Count
nLastColumn = 0
msg = ""
For Each rr In r
N = rr.Column
If N < nFirstColumn Then nFirstColumn = N
If N > nLastColumn Then nLastColumn = N
Next rr
For N = nFirstColumn To nLastColumn
Set rInt = Intersect(Columns(N), r)
If rInt Is Nothing Then
Else
For Each rr In rInt
msg = msg & vbCrLf & rr.Address(0, 0)
Next rr
End If
Next N
MsgBox msg
End Sub

Excel VBA swapping columns of a vba range without a loop

I have a range consisting of two columns that the user would define thru Application.Inputbox method. I would store that as rng in the VBA to be copied then pasted later to some cells in Excel sheet. Before pasting, I would like to swap these two columns in rng. Is there a way to do that without a loop and without having to swap the actual original columns in the excel sheet?
So what I mean is something like this:
rng_swapped.Columns(1).Value = rng.Columns(2).Value
rng_swapped.Columns(2).Value = rng.Columns(1).Value
rng = rng_swapped
Use a variant array as an intermediate temporary storage so you can overwrite the original.
dim arr as variant
arr = rng_swapped.Columns(1).value
rng_swapped.Columns(1) = rng_swapped.Columns(2).Value
rng_swapped.Columns(2) = arr
from your narrative my understanding is that the range to paste to is different from the range to copy from.
so just go like this
Dim rng As Range
Set rng = Application.InputBox("Please select a range:", "Range Selection", , , , , , 8)
Dim rngToPaste As Range
Set rngToPaste = rng.Offset(, 20) ' just a guess...
rngToPaste.Columns(1).Value = rng.Columns(2).Value
rngToPaste.Columns(2).Value = rng.Columns(1).Value
How to use Jeeped's code
While playing around with the code... my curiosity fires away:
Why not:?
arr1 = oRng.Columns(1)
arr2 = oRng.Columns(2)
oRng.Columns(1) = arr2
oRng.Columns(2) = arr1
It turns out something (probably) the extra line makes the code slower (by about 10%).
I have a similar scenario and I know the range address. How should I use the code?
Sub SwapColumnsRange()
'Description
'In a specified range, swaps the first two columns i.e. the values of
'column(1) become the values of column(2) and the values of column(2) become
'the values of column(1).
'Arguments as constants
'cStrRange
'A string containing the Address of the range to be processed.
Const cStrRange As String = "A1:B50000" 'Your range address here.
Dim arr As Variant
Dim oRng As Range
Set oRng = Range(cStrRange)
If oRng.Areas.Count > 1 Then Exit Sub
If oRng.Columns.Count < 2 Then Exit Sub
'Slightly modified Jeeped's code
arr = oRng.Columns(1) '.Value
oRng.Columns(1) = oRng.Columns(2).Value
oRng.Columns(2) = arr
End Sub
I forgot to mention that I have more than two columns to be swapped!?
Sub ShiftColumnsRangeLeft()
'Description
'In a specified range with columns from 1 to 'n', shifts columns to the left
'i.e. the values of column(1) become the values of column(n), the values of
'column(2) become the values of column(1)... ...the values of column(n), the
'last column, become the values of column(n-1).
'Arguments as constants
'cStrRange
'A string containing the Address of the range to be processed.
Const cStrRange As String = "A1:I50000" 'Your range address here.
Dim arr As Variant
Dim oRng As Range
Dim i As Integer
Set oRng = Range(cStrRange)
If oRng.Areas.Count > 1 Then Exit Sub
If oRng.Columns.Count < 2 Then Exit Sub
For i = 1 To oRng.Columns.Count - 1 'ShiftColumnsRangeRight Difference
'Slightly modified Jeeped's code
arr = oRng.Columns(i) '.Value
oRng.Columns(i) = oRng.Columns(i + 1).Value
oRng.Columns(i + 1) = arr
Next
End Sub
You're a little off topic here, aren't you?
But not to this side, to the other side, please!?
Sub ShiftColumnsRangeRight()
'Description
'In a specified range with columns from 1 to 'n', shifts columns to the right
'i.e. the values of column(1) become the values of column(2), the values of
'column(2) become the values of column(3)... ...the values of column(n), the
'last column, become the values of column(1).
'Arguments as constants
'cStrRange
'A string containing the Address of the range to be processed.
Const cStrRange As String = "A1:I50000" 'Your range address here.
Dim arr As Variant
Dim oRng As Range
Dim i As Integer
Set oRng = Range(cStrRange)
If oRng.Areas.Count > 1 Then Exit Sub
If oRng.Columns.Count < 2 Then Exit Sub
For i = oRng.Columns.Count - 1 To 1 Step -1 'ShiftColumnsRangeLeft Difference
'Slightly modified Jeeped's code
arr = oRng.Columns(i) '.Value
oRng.Columns(i) = oRng.Columns(i + 1).Value
oRng.Columns(i + 1) = arr
Next
End Sub
I've changed my mind, I want to select a range and then run the macro to shift the columns!?
Sub ShiftColumnsSelectionRight()
'Description
'In a selection with columns from 1 to 'n', shifts columns to the right
'i.e. the values of column(1) become the values of column(2), the values of
'column(2) become the values of column(3)... ...the values of column(n), the
'last column, become the values of column(1).
Dim arr As Variant
Dim oRng As Range
Dim i As Integer
Set oRng = Selection
If oRng.Areas.Count > 1 Then Exit Sub
If oRng.Columns.Count < 2 Then Exit Sub
For i = oRng.Columns.Count - 1 To 1 Step -1 'ShiftColumnsRangeLeft Difference
'Slightly modified Jeeped's code
arr = oRng.Columns(i) '.Value
oRng.Columns(i) = oRng.Columns(i + 1).Value
oRng.Columns(i + 1) = arr
Next
End Sub
I've had it! Do the other two versions (Swap & ShiftLeft) yourself!
Remarks
These examples demonstrate how by making some simple modifications, the code can be used in different scenarios.
50000 is used to emphasize that the handling of the initial problem by looping through the range instead of using an array gets much, much slower as more rows are in the range.
The first If Statement ensures that the range is contiguous, and the second one ensures that there are at least two columns in the range.
Issues
I'm not completely sure that the '.value' part in the first line is not needed, but the code worked fine so far. On the other hand the '.value' part in the second line is needed or empty cells will be transferred.
When there are formulas in the range, they will be lost i.e. values will be transferred instead.

Copy and paste loop through 2 sets of ranges

I am working on an excel sheet. I have titles of data entries, which I wish to copy to other locations on the sheet. The titles range from c3 to c122, and I intend to copy and paste them in cells 70 rows apart. The code below doesn't suffice; I am given error 450 (wrong number of arguments or invalid property assignment).
Option Explicit
Sub Titles()
Dim i As Integer
Dim n As Integer
For i = 151 To 8971 Step 70
For n = 3 To 122 Step 1
Cells.Value(i, 3) = "c" & n
Next n
Next i
End Sub
I desire to have what is in each of cells C3:C122 to be copied into C150, C220,...,C8791. Is this a good approach? Thank you for your help.
There is a lot wrong here.
The double for loop is not required. If the code did work, it would overwrite Cells.Value(i, 3) 120 times on each iteration of i
Cells on its own refers to all the cells on the Active Sheet
Cells.Value returns the cell values as a variant array, in this case a 1,048,576 x 16,384 array
"c" & n creates a string (eg c3) not a cell reference
Try someting like this (note, please check the initial value of rw as there is a contradiction in your post 150 vs 151)
Sub Demo()
Dim ws As Worksheet
Dim rng As Range
Dim cl As Range
Dim rw As Long
Set ws = ActiveSheet
Set rng = ws.Range("C3:C122")
rw = 151 '<--- check if this is right
For Each cl In rng.Cells
ws.Cells(rw, 3).Value = cl.Value
rw = rw + 70
Next
End Sub
Syntax on Cells.Value(i, 3) is wrong it should be Cells(i, 3).Value
Change that and see if everything works as you wanted.

Resources