Repeat a column list in Excel until end of sheet - vba

I feel like this is extremely simple, but for some reason, this is not an easy thing to do in Excel. Basically I have a vertical set of values running down a column ranging from 1 to 3, but they are random. However this same sequence will repeat every 60 rows. I need the sequence to just repeat itself every 60 rows, as the data file I'm working on is tens of thousands of rows and I don't feel like copying and pasting that many times. The values run from M2:M61.
Is there a simple VBA code to loop this column?
Alternatively, is there an equation that can do this? I tried using Index, but don't think I was specifying the row or columns correctly
=INDEX($M$122:$M$181,ROUNDUP(ROWS(I$122:I132)/60,0),13)

If you wanted some code to do it, maybe this will give you some ideas. It assumes that your first lot of 60 values have been entered in A1:A60 and reads them into an array, and then loops up to the number of loops (I used 12000) copying the values in by steps of 60.
Sub Repeat60Rows()
Dim ListArray As Variant
Dim Loopcounter As Long
Dim MyRange As String
ListArray = Worksheets("Sheet2").Range("A1:A60").Value
For Loopcounter = 1 To 12000 Step 60
MyRange = "A" & Loopcounter & ":A" & Loopcounter + 59
Worksheets("Sheet2").Range(MyRange) = ListArray
Next Loopcounter
End Sub

I learned a little more and realized, because this is a repeating set of 60 numbers, I could just use a simple =OFFSET(M62, -60, 0) formula and I could just paste down. I'm sure there's probably an easier way to automate this with VBA, but I'm unsure as to how to do that.

In M182
=OFFSET(M$121,MOD(ROW()-2,60)+1,0)
and fill down

Related

VBA Get Values from AutoFilter

Let's say I have very large set of data with over 100,000+ rows. In Column A, I want to find each unique number.
I understand this can be done using the .Find feature and Collections/Arrays but those seem to take a good bit of time - especially with 100,000+ rows.
However, after AutoFiltering Column A, when I hit the down arrow it displays only unique variables. Is it possible to simply extract those values out of the selections in this way?
'pseudocode
filter.Count
Dim X As Long
For x = 2 to filter.Count
Cells(x, 14) = filter(x)
Next x
You can use advanced filter, it's pretty darn quick. I tried it with 127k rows, the results were instant.
Columns("A:A").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("D1"), Unique:=True
You can extract the visible cells in to an array. Say your total range (without filter) is A2:A10000. Run your filter, then you can run this macro:
Sub t()
Dim arr() As Variant
arr = Range("A2:A10000").SpecialCells(xlCellTypeVisible)
Dim i As Long
For i = LBound(arr) To UBound(arr)
Debug.Print (arr(i, 1))
' Do things with each entry in array
Next i
End Sub

How to use variables in regular MS excel expressions

My research shows that I need to use Visual Basic. I am a programmer/developer, but have never used VB so if anyone could dumb it down it would be appreciated.
Here's my working excel function:
=IF(MATCH(1,E1:DP1,0),D1,FALSE)
I want to loop a few of those numbers such that:
=IF(MATCH(141,E1:DP378,0),D378,FALSE)
THEN take my answers (which will be strings, because column D are all strings, the rest of the excel file are numbers)
=CONCAT
end goal: have 141 String arrays populated based on the data in my table.
I went ahead and made my first attempt at VBA like this:
Sub myFunc()
'Initialize Variables
Dim strings As Range, nums As Integer, answer() As Variant, listAnswers() As Variant
'set variables
strings = ("C1:C378")
nums = 141
i = 0
j = 0
ReDim Preserve answer(i)
ReDim Preserve listAnswers(j)
'answer() = {""}
'for each in nums
For counter = 0 To nums
ReDim Preserve listAnswers(0 To j)
'set each list of answers
listAnswers(i) = Join(answer(), "insertJSONcode")
j = j + 1
'for each in Stings
For Each cell In strings
If cell <> "" Then
ReDim Preserve answer(0 To i)
answer(i) = 'essentially this: (MATCH(2,E1:DP1,0),D1,FALSE)
i = i + 1
end If
next cell 'end embedded forEach
Next LCounter 'end for loop
'is this possible? or wrong syntax?
Range("A:A").Value = listAnswers() ' should print 141 arrays from A1 to A141
End Sub
EDIT:
Important note I do NOT need to call the sheet by Name. I've successfully written integer values to by excel sheet in column A without doing so.
Also, the VBA I wrote I was never intended to work, I know it's broken at least where answer(i) is supposed to write to something. I'm only putting that code there to show I was able to at least able to get into spitting distance of the proper logic and prove I've put some serious effort into solving the problem and give a rough starting point.
Here's an image of the excel format. Column C goes down to 378 and the numbers listed from E through DP are populated by a database. It consists of blank cells and numbers between 1 and 141.
Looking back at my if statement:
=IF(MATCH(2,E2:DP2,0),D2,FALSE)
If I were to type that exactly into cell B2 it would output the correct answer "text2". which is neat and all, but I need every instance of text 2 written out, then CONCAT those results. Easy so far, I could drag that down all the way through column B and have all of my "text" strings in one column, CONCAT that column and there's the answer. However I don't just need #2, I need each number between 1 and 141. Plus I want to avoid writing 141 columns with a CONCAT on top of each one.

vba - Macro producing incorrect results when run, but when stepping into results are correct

I have a macro that inserts a VLOOKUP into a column. The macro has to take a number stored as text and convert it to a number, before looking up that number in another sheet.
The macro always produces the same results, such as reaching row 43 before starting to produce erroneous results however when using F8 to step through the code, these incorrect results are not produced.
The erroneous results are that the value placed into col 13 is not equal to the number stored as text. Mostly it seems as though values from rows above and below, sometimes 2 rows below are being inserted to col 13. Almost seems to me as if 2 different threads are running at 2 different speeds or something?
If anyone could have a look at the loop causing the errors I would be grateful, thanks.
For counter = 2 To NumRowsList
checker = CInt(Sheets("Sheet2").Cells(counter, 3)
Sheets("Sheet2").Cells(counter, 13).Value = checker
'Call WaitFor(0.5)
If checker < 4000 Then
Sheets("Sheet2").Cells(counter, 14) = "=VLOOKUP(M" & counter & ",Sheet4!E2:F126,2,FALSE)"
Else
Sheets("Sheet2").Cells(counter, 14) = "=VLOOKUP(M" & counter & ",Sheet5!B2:C200,2,FALSE)"
End If
Next counter
I have tried a few similar variations of this code, such as using the value stored in col 13 directly rather than using the cell reference in the VLOOKUP, always producing the same results.
I even used the waitfor function to try and create a delay hoping it may synchronise the operations, but it did not help and using a delay of more than 0.5 would cause the run time of the macro to be too big.
UPDATE:
I did not find a perfect solution, only a long hand work around. I simply combined the Vlookups onto a single sheet, and converted the numbers stored as text to numbers outside of the vba routine. This took the error away from the number calculation (just col C * 1), and then the vlookups were looking up the correct values. Thank you for the help, regardless.
you can avoid looping, checker and all those If-Then-Else, like follows
edited to account for VlookUp range depending on VlookUp value
With Worksheets("Sheet2")
.Range("N2", .Cells(NumRowsList, 14)).FormulaR1C1 = "=VLOOKUP(Value(RC3),IF(Value(RC3)<4000,Sheet4!R2C5:R126C6,Sheet4!R2C2:R200C3),2,FALSE)"
End With
The following works for me with my test data, but you'll need to see if it works for you... (also are you turning off calculation or events? I don't know if this might have an issue?)
I find it preferable to set a reference to the sheet you want to use rather than access it directly, and this may help?
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet2")
Dim VLURange As String, checker As Long
For counter = 2 To 200 ' NumRowsList
checker = CLng(ws.Cells(counter, 3).Value)
ws.Cells(counter, 13) = checker
VLURange = IIf(checker < 4000, "Sheet4!E2:F126", "Sheet5!B2:C200")
ws.Cells(counter, 14) = "=VLOOKUP(M" & counter & ", " & VLURange & ", 2, FALSE)"
Next counter

VBA - merge set number of rows in first column

I have seen some VBA examples on here allowing one to merge set numbers of cells, but none exactly as I need it.
What I would like to do is go down the entire column A:A and merge every four rows, starting with cell A4. I know this involves changing the reference cell but I'm not skilled enough with the language to know how to do this without screwing up the cycle.
Here is an example of the data I would like to format. Thanks in advance for any and all help with this.
Simply set Count to the number of merged cells that you want and run the MergeColA.
Sub MergeColA()
Dim Count As Integer
Count = 10
MergeCells (Count)
End Sub
Sub MergeCells(Count As Integer)
For i = 4 To 4 * count Step (4)
Dim r As Range
Set r = Range("A" & i, "A" & i + 3)
r.Merge
Next i
End Sub

Most efficient way to change values conditionally in a range?

I'm just wanting to run through a (large) range and replace certain values (if they're above a given max or below a given min...also one particular character) with a given replacement value.
My first thought is to simply traverse each cell and check/replace when necessary. I have a feeling this procedure would be really slow though, and I'm curious if there's a better way to accomplish this.
Any time I write code that does something similar to this in VBA I watch each cell have its value altered cell by cell and it seems like there must be better way. Thanks in advance.
edit:
I haven't even written this implementation yet because I know what the result will be and I would rather do something different if it's possible, but here's what it would look like
For something
If(Range.Value == condition)
Range.Value = replacement_value
Range = Range.Offset(a, b)
End For
Make a formula in a separate column, and then copy/paste special, values only.
= if(A2 > givenvalue; replace; if(A2< anothergivenvalue; anotherreplace; if (A2 = "particularcharacterortext"; replaceonemore; A2)))
Put the formula in an empty cell in an empty column, drag it or copy/paste to the entire column. After that, if the new values are ok, copy/paste values only to the original position.
The following VBA code provides a simple framework that you can customize to meet your needs. It incorporates many of the optimizations that have been mentioned in the comments to your question, such turning off screen updating and moving the comparison from the worksheet to an array.
You will notice that the macro does a rather large compare and replace. The data set I ran it on was 2.5 million random numbers between 1 and 1000 in the range A1:Y100000. If a number was greater than 250 and less than 500, I replaced it with 0. This required replacing 24.9 percent of all the numbers in the data set.
Sub ReplaceExample()
Dim arr() As Variant
Dim rng As Range
Dim i As Long, _
j As Long
Dim floor as Long
Dim ceiling as Long
Dim replacement_value
'assign the worksheet range to a variable
Set rng = Worksheets("Sheet2").Range("A1:Y100000")
floor = 250
ceiling = 500
replacement_value = 0
' copy the values in the worksheet range to the array
arr = rng
' turn off time-consuming external operations
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
'loop through each element in the array
For i = LBound(arr, 1) To UBound(arr, 1)
For j = LBound(arr, 2) To UBound(arr, 2)
'do the comparison of the value in an array element
'with the criteria for replacing the value
If arr(i, j) > floor And arr(i, j) < ceiling Then
arr(i, j) = replacement
End If
Next j
Next i
'copy array back to worksheet range
rng = arr
'turn events back on
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
End Sub
I did some performance testing on different alternatives for coding this simple compare and replace, with results that I would expect are consistent with VBA performance results by others. I ran each alternative 10 times, calculating the elapsed time for each run, and averaging the 10 elapsed times.
The results reveal the large impact that using arrays can have, especially when the data set is large: Compared to code that tested and changed worksheet cell values one-by-one, the array operation -- copying the data set from the worksheet into an array, comparing and changing the array values, and then writing the array results back to the worksheet -- in this case reduced average run times by 98 percent, from 3.6 minutes to 4 seconds.
While the optimizations that turned off external events made a noticeable difference in worksheet operations, with a 22 percent reduction in run times, those optimizations had very little impact when most of the computational work is array-based.