excel vba loop through worksheets fails - vba

I don't know why this function doesn't loop through the worksheets , what am I missing ?
I've gone through Almost every resource I can find both on stack overflow and Google but could not find an answer that I could implement.
I've tried looping through worksheet numbers however that didn't work so I am now attempting to loop through worksheet names. This also does not work.
I'm pretty sure it's a small error but I could not find the cause after days of searching.
Sub CreateUniquesList()
Dim WS_Count As Integer 'number of WorkSheets
Dim Sheet As Integer 'WorkSheet number
Dim Uniques() As String 'Array of all unique references
Dim UniquesLength As Integer
Dim size As Integer 'number of items to add to Uniques
Dim Row As Integer 'row number
Dim Column As Variant 'column number
Dim Columns As Variant
Dim blanks
Dim LastRow As Integer
Dim i As Integer
Dim wks As Variant, wksNames() As String
WS_Count = ActiveWorkbook.Worksheets.Count
ReDim wksNames(WS_Count - 1)
i = 0
For Each wks In Worksheets
wksNames(i) = wks.Name
i = i + 1
Next
Columns = Array(3, 4, 8, 11, 12, 17, 18)
ReDim Uniques(0)
Uniques(0) = "remove this item"
WS_Count = ActiveWorkbook.Worksheets.Count
' For Sheet = 1 To WS_Count
For Each wks In wksNames
For Each Column In Columns
' LastRow = ActiveWorkbook.Worksheets(Sheet).Cells(Rows.Count, Column).End(xlUp).Row
' size = WorksheetFunction.CountA(Worksheets(Sheet).Columns(Column)) - 1
LastRow = ActiveWorkbook.Worksheets(wks).Cells(Rows.Count, Column).End(xlUp).Row
size = WorksheetFunction.CountA(Worksheets(wks).Columns(Column)) - 1
UniquesLength = UBound(Uniques) - LBound(Uniques) + 1
ReDim Preserve Uniques(UniquesLength + size - 1)
blanks = 0
i = 1
For Row = LastRow To 2 Step -1
If Cells(Row, Column).Value <> "" Then
Uniques(UniquesLength + i - 1 - blanks) = Cells(Row, Column).Value
Else
blanks = blanks + 1
End If
i = i + 1
Next Row
Next Column
Next wks
' Next Sheet
'remove first unique element
For i = 1 To UBound(Uniques)
Uniques(i - 1) = Uniques(i)
Next i
ReDim Preserve Uniques(UBound(Uniques) - 1)
End Sub

I took a look at the code and have rewritten a fair portion of it as I don't think a lot of it was necessary (probably leftover from your attempts to make things work). Try this, and if you don't understand any of it, post a comment and I'll explain further.
Sub CreateUniquesList()
Dim Uniques() As String 'Array of all unique references
Dim Row As Integer 'row number
Dim Column As Variant 'column number
Dim Columns As Variant
Dim LastRow As Integer
Dim wks As Worksheet
Columns = Array(3, 4, 8, 11, 12, 17, 18)
ReDim Uniques(0)
For Each wks In ThisWorkbook.Worksheets
For Each Column In Columns
LastRow = wks.Cells(wks.Rows.Count, Column).End(xlUp).Row
For Row = LastRow To 2 Step -1
If wks.Cells(Row, Column).Value <> "" Then
Uniques(UBound(Uniques)) = wks.Cells(Row, Column).Value ' set the last element of the array to the value
ReDim Preserve Uniques(UBound(Uniques)+1) ' increment the size of the array
End If
Next Row
Next Column
Next wks
' lose the last element of the array as it's one larger than it needs to be
ReDim Preserve Uniques(UBound(Uniques) - 1)
End Sub

Try this
WS_Count = ActiveWorkbook.Worksheets.Count
' For Sheet = 1 To WS_Count
For Each wks In Worksheets
For Each Column In Columns
'LastRow = ActiveWorkbook.Worksheets(Sheet).Cells(Rows.Count,column).End(xlUp).Row
'size = WorksheetFunction.CountA(Worksheets(Sheet).Columns(Column)) - 1
LastRow = ActiveWorkbook.Worksheets(wks.Name).Cells(Rows.Count,Column).End(xlUp).Row
size = WorksheetFunction.CountA(Worksheets(wks.Name).Columns(Column)) - 1
UniquesLength = UBound(Uniques) - LBound(Uniques) + 1
ReDim Preserve Uniques(UniquesLength + size - 1)
blanks = 0
i = 1
For Row = LastRow To 2 Step -1
If Cells(Row, Column).Value <> "" Then
Uniques(UniquesLength + i - 1 - blanks) = Cells(Row, Column).Value
Else
blanks = blanks + 1
End If
i = i + 1
Next Row
Next Column
Next wks

Related

VBA - Breaking a cell string into individual cells while preserving character format

I have a cell with a string of different lengths. I want split them into individual cells with a length of, say, 3 characters.
A cell with ABCCBA should end up ABC CBA in 2 different cells.
While a cell with ABCDABCDAB should end up ABC DAB CDA B in 4 different cells.
In addition to that, some of the characters are italic, and I want to preserve the character format in the individual cells.
Is there any convenient way to do this?
Using Mid() function in both VBA or formulas works but it didn't preserve the character format.
I tried the following, but the code gives an error.
' Finding number of cells
Segments = WorksheetFunction.RoundUp(Len(Range("A1").Value) / 3, 0)
' Split base on character length
For n = 1 to Segments
Cells(2, n) = Range("A1").Characters(1 + (n - 1) * 3, 3)
Next n
I ended up doing something like this:
' Finding number of cells
Segments = WorksheetFunction.RoundUp(Len(Range("A1").Value) / 3, 0)
LenCel = Len(Range("A1").Value)
' Split base on character length
For n = 1 To Segments
Range("A1").Copy
Cells(2, n).PasteSpecial Paste:=xlPasteAllUsingSourceTheme
Cells(2, n).Characters(1, (n - 1) * 3).Delete
Cells(2, n).Characters(3 + 1, LenCel).Delete
Next n
I used .PasteSpecial to main the character format and then .Delete the characters. Not elegant, but does the job.
Does this work for you.
Public Sub FormatGroupings()
Dim wb As Workbook
Dim ws As Worksheet
Dim inputString As String
Dim Segments As Long
Dim formatCollection As New Collection
Dim charNum As Long
Dim Group As Long
Set wb = ThisWorkbook
Set ws = wb.WorkSheets("Sheet1")
inputString = ws.Range("A1")
Segments = WorksheetFunction.RoundUp(Len(inputString) / 3, 0)
With ws
For charNum = 1 To Len(inputString)
If .Range("A1").Characters(Start:=charNum, Length:=1).Font.FontStyle = "Italic" Then
formatCollection.Add "Italic"
Else
formatCollection.Add "Regular"
End If
Next charNum
Dim counter As Long
counter = 1
For Group = 1 To Segments
.Cells(2, Group) = Mid$(inputString, 1 + (Group - 1) * 3, 3)
For charNum = 1 To Len(.Cells(2, Group))
.Cells(2, Group).Characters(Start:=charNum, Length:=1).Font.FontStyle = formatCollection(counter)
counter = counter + 1
Next charNum
Next Group
End With
End Sub
Or using an Array which is possibly faster:
Public Sub FormatGroupings2()
Dim wb As Workbook
Dim ws As Worksheet
Dim inputString As String
Dim Segments As Long
Dim formatArr()
Dim charNum As Long
Dim Group As Long
Set wb = ThisWorkbook
Set ws = wb.WorkSheets("Sheet1")
inputString = ws.Range("A1")
ReDim formatArr(Len(inputString))
Segments = WorksheetFunction.RoundUp(Len(inputString) / 3, 0)
With ws
For charNum = 1 To Len(inputString)
If .Range("A1").Characters(Start:=charNum, Length:=1).Font.FontStyle = "Italic" Then
formatArr(charNum - 1) = "Italic"
Else
formatArr(charNum - 1) = "Regular"
End If
Next
Dim counter As Long
counter = 0
For Group = 1 To Segments
.Cells(2, Group) = Mid$(inputString, 1 + (Group - 1) * 3, 3)
For charNum = 1 To Len(.Cells(2, Group))
.Cells(2, Group).Characters(Start:=charNum, Length:=1).Font.FontStyle = formatArr(counter)
counter = counter + 1
Next charNum
Next Group
End With
End Sub

remove blanks from combobox with two lists

I'm trying to remove the blank records from a combobox with two lists.
This is my code:
Private Sub UserForm_Initialize()
Dim N As Range
Dim LastRow As Integer
Dim ws As Worksheet
PREST.ColumnCount = 2
Set ws = Worksheets("L_Location")
LastRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Dim i, j As Integer
Dim location(2 To 100, 1 To 2) As String
For j = 1 To 2
For i = 2 To LastRow
If ws.Cells(i, j).Value <> vbNullString Then
location(i, j) = ws.Cells(i, j).Value
End If
Next i
Next j
PREST.List = location
End Sub
I don't know what I'm doing wrong.
You are having blanks because your 2D array is already sized with 100 rows. A simple workaround would be to first count the non-empty rows, then dimension the Array accordingly.
Dim location() As String
Dim count As Long
count = Range("A2:A" & LastRow).SpecialCells(xlCellTypeConstants).Cells.count
ReDim location(1 To count, 1 To 2)
'then continue from here to fill the array
This code will fill the combobox with your range value then will delete any empty item:
Private Sub UserForm_Initialize()
Dim LastRow As Long
Dim ws As Worksheet
PREST.ColumnCount = 2
Set ws = Worksheets("L_Location")
LastRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Dim i As Long ', j As Integer
PREST.List = ws.Range("a1:b" & LastRow).Value
For i = PREST.ListCount - 1 To 0 Step -1
If PREST.List(i) = "" Then PREST.RemoveItem i
Next
End Sub
I tried this :
Dim location() As String
ReDim location(LastRow - 2, 1)
For j = 0 To 1
For i = 0 To LastRow - 2
If ws.Cells(i + 2, j + 1).Value <> vbNullString And ws.Cells(i + 2, j + 1).Value <> "" Then
location(i, j) = ws.Cells(i + 2, j + 1).Value
End If
Next i
Next j
PREST.List = location
which seems to work but i guess its gonna give me an error if the list is empty (lastrow = 1)
Since you say that any two cells on the same row are both either blank or with values, then you could go like follows:
Dim cell As Range
Dim i As Long, j As Long
PREST.ColumnCount = 2
With Worksheets("L_Location") '<--| reference your worksheet
With .Range("A2", .Cells(.Rows.Count,1).End(xlUp)).SpecialCells(xlCellTypeConstants) '<--| reference its column A not empty cells from row 1 down to last not empty one
Dim location(1 To .Count, 1 To 2) As String '<--| size your array rows number to that of referenced cells
For Each cell In .Cells '<--| loop through referenced cells
i = i + 1 '<--| update array row index
For j = 1 To 2 '<--| loop through array columns
location(i, j) = cell.Offset(j -1).Value '<--| fill array
Next j
Next cell
End With
End With
PREST.List = location

Subroutine returning all 0s

I am trying to return the values from a user defined function, but all that is returned are 0s. I feel like the values I'm assigning to the variables wk1 and wk2 aren't being used in the function.
The goal of the subroutine is to calculate the weekly returns of stocks, given the prices provided in Worksheet "Prices".
I'm not very savvy with VBA so any help is appreciated
Thanks for the help!
Public Sub wklyrtn()
Dim wk1, wk2 As Long
Dim row As Long, column As Long
Dim matrix1(2 To 261, 2 To 11) As Integer
Sheets("Prices").Select
Selection.Activate
For row = 2 To 261
For column = 2 To 11
wk2 = Cells(row, column).Value
wk1 = Cells(row + 1, column).Value
matrix1(row, column) = Rtrn(wk1, wk2)
Next column
Next row
Sheets("Returns").Select
Selection.Activate
For row = 2 To 261
For column = 2 To 11
Cells(row, column) = matrix1(row, column)
Next column
Next row
End Sub
Public Function Rtrn(wk1, wk2)
Dim delt As Long
Application.Volatile True
delt = wk2 - wk1
Rtrn = delt / wk1
End Function
Try this. Not sure what you are trying to do with the Matrix. But this will give you the values you need. You need to refer to an object (your worksheets) instead of using select (always avoid that in general and try to refer to an object instead by using set.
Option Explicit
Public Sub wklyrtn()
Dim wk1 As Long, wk2 As Long
Dim row As Long, column As Long
Dim matrix1(2 To 261, 2 To 11) As variant
Dim wks As Worksheet, wks2 As Worksheet
Set wks = ThisWorkbook.Sheets("Prices")
With wks
For row = 2 To 261
For column = 2 To 11
wk2 = wks.Cells(row, column).Value
wk1 = wks.Cells(row + 1, column).Value
matrix1(row, column) = Rtrn(wk1, wk2)
Next column
Next row
End With
Set wks2 = ThisWorkbook.Sheets("Returns")
With wks2
For row = 2 To 261
For column = 2 To 11
wks2.Cells(row, column) = matrix1(row, column)
Next column
Next row
End With
End Sub
Public Sub Get_Price_Index_Var()
Dim lRow As Long, bCol As Byte 'Avoid naming varaibles same as VBA objects, properties, etc
Dim vResults(2 To 261, 2 To 11) As Variant 'Using variant datatype gives flexibility to hold the result of the operations perform
Dim aResults As Variant 'Used get resulting array to be enter in Returns Worksheet
Dim vVal1 As Variant, vVal2 As Variant
With ThisWorkbook.Sheets("Prices")
For lRow = 2 To 261
For bCol = 2 To 11
vVal1 = .Cells(lRow, bCol).Value
vVal2 = .Cells(1 + lRow, bCol).Value
vResults(lRow, bCol) = fReturns(vVal1, vVal2)
Next: Next: End With
aResults = WorksheetFunction.Index(vResults, 0, 0)
With ThisWorkbook.Sheets("Returns")
.Cells(2, 2).Resize(UBound(aResults), UBound(aResults, 2)).Value = aResults
End With
End Sub
Public Function fReturns(vVal1 As Variant, vVal2 As Variant) As Variant
fReturns = (vVal2 - vVal1) / vVal1
End Function
other than working with Variant variables (as you've already been told), you can take advantage of using arrays to both speed up macro execution and shorten down your code:
Option Explicit
Public Sub wklyrtn()
Const ROWMIN As Long = 2
Const ROWMAX As Long = 261
Const COLMIN As Long = 2
Const COLMAX As Long = 11
Dim row As Long, column As Long
Dim data As Variant, matrix1 As Variant
With Sheets("Prices")
data = .Range(.Cells(ROWMIN, COLMIN), .Cells(ROWMAX + 1, COLMAX)).Value '<--| read all needed values into 'data' array (it needs one row more at the bottom)
End With
ReDim matrix1(1 To ROWMAX - ROWMIN + 1, 1 To COLMAX - COLMIN + 1) As Double '<--| size returned valuse array accordingly to chosen rows and column indexes ranges
For row = 1 To ROWMAX - ROWMIN + 1
For column = 1 To COLMAX - COLMIN + 1
matrix1(row, column) = Rtrn(data(row + 1, column), data(row, column)) '<-- store returned values into 'matrix1' array
Next column
Next row
Sheets("Returns").Cells(2, 2).Resize(ROWMAX - ROWMIN + 1, COLMAX - COLMIN + 1 + 1).Value = matrix1 '<--| write returned values from 'matrix1' array into cells
End Sub
Public Function Rtrn(wk1, wk2)
Dim delt As Long
Application.Volatile True
delt = wk2 - wk1
Rtrn = delt / wk1
End Function

Storing Result like an array with countif in VBA excel

I am assigning numbers their order in which they appear in the list and i do that using countif function in excel something like this,
=COUNTIF(A$2:A2,A2)
Number Count
10 1
10 2
10 3
11 1
11 2
11 3
12 1
I wish to achieve the same using VBA. However, here are the specifics.
I want to take a variable and compute the countif function and then loop them through.
Once the variable has all numbers(array) I want to paste them in a location.
Assuming column A is sorted as per your list above you could use the following.
Dim arr(100,1) as double '100 = arbitrary number for this example
dim n as double
n=1
arr(roW,0) = Cell(roW + 2, 1).value
arr(roW,1) = n
For roW = 1 to 100
IF Cell(roW + 2, 1).value = Cell(roW + 1, 1).value Then
n = Cell(roW + 2, 1).value
Else
n=1
End if
arr(roW,0) = Cell(roW + 2, 1).value
arr(roW,1) = n
Next
Range("C2:D102")=arr
And another option,
Sub GetUniqueAndCountif()
Dim cUnique As Collection
Dim Rng As Range
Dim Cell As Range, nW As Range
Dim sh As Worksheet
Dim vNum As Variant
Set sh = ThisWorkbook.Sheets("Sheet1")
Set Rng = sh.Range("A2", sh.Range("A2").End(xlDown))
Set cUnique = New Collection
On Error Resume Next
For Each Cell In Rng.Cells
cUnique.Add Cell.Value, CStr(Cell.Value)
Next Cell
On Error GoTo 0
For Each vNum In cUnique
Set nW = Cells(Rows.Count, "C").End(xlUp).Offset(1, 0)
nW = vNum
nW.Offset(, 1) = WorksheetFunction.CountIf(Rng, nW)
Next vNum
End Sub
The following code evaluates the results as a single array formula and assigns this to a varaiable v. You can adapt references and add variable declarations as needed.
Sub CountifArray()
v = Evaluate(Replace("INDEX(COUNTIF(OFFSET(y,,,ROW(y)-MIN(ROW(y))+1),y),)", "y", "A2:A8"))
Range("B2:B8") = v
End Sub
This is my suggestion.
Sub Counts()
Dim ws As Worksheet
Set ws = ThisWorkbook.ActiveSheet
Dim lngLastRow As Long
lngLastRow = ws.UsedRange.Rows.Count
Dim Arr() As Variant
'Taking values in column A into an array
Arr = ws.Range("A2:A" & lngLastRow).Value
Dim Arr2() As Variant
'another Array for Countif results
ReDim Arr2(lngLastRow - 2, 0)
Dim count As Long
Dim i As Long, j As Long 'counters
'counting
For i = LBound(Arr) To UBound(Arr)
count = 0
For j = LBound(Arr) To i
If Arr(j, 1) = Arr(i, 1) Then count = count + 1
Next
'filling the array with results
Arr2(i - 1, 0) = count
Next
'sending results back to the worksheet
ws.Range("B2:B" & lngLastRow).Value = Arr2
Set ws = Nothing
End Sub

how to insert a row before pasting an array

I currently have an array which I populate and paste in a sheet named "T1" using a macro. My current macro uses the rowcount function to determine the used rows and pastes the array from the next available row.
The problem I am having is that when I paste this array multiple times, the arrays need to be spaced by a row so that i can differentiate different submissions. This is what I have so far, and I was hoping someone could help me with this:
Sub CopyData()
Dim Truearray() As String
Dim cell As Excel.Range
Dim RowCount1 As Integer
Dim i As Integer
Dim ii As Integer
Dim col As Range
Dim col2 As Range
i = 0
ii = 2
RowCount1 = DHRSheet.UsedRange.Rows.Count
Set col = DHRSheet.Range("I1:I" & RowCount1)
For Each cell In col
If cell.Value = "True" Then
Dim ValueCell As Range
Set ValueCell = Cells(cell.Row, 3)
ReDim Preserve Truearray(i)
Truearray(i) = ValueCell.Value
Dim siblingCell As Range
Set siblingCell = Cells(cell.Row, 2)
Dim Siblingarray() As String
ReDim Preserve Siblingarray(i)
Siblingarray(i) = DHRSheet.Name & "$" & siblingCell.Value
i = i + 1
End If
Next
Dim RowCount2 As Integer
RowCount2 = DataSheet.UsedRange.Rows.Count + 1
For ii = 2 To UBound(Truearray)
DataSheet.Cells(RowCount2 + ii, 2).Value = Truearray(ii)
Next
For ii = 2 To UBound(Siblingarray)
DataSheet.Cells(RowCount2 + ii, 1).Value = Siblingarray(ii)
Next
DataSheet.Columns("A:B").AutoFit
MsgBox ("Data entered has been successfully validated & logged")
End Sub
If you Offset two rows from the bottom cell, you will leave a blank row of separation. You should also consider filling the whole array as base 1 and writing it to DataSheet in one shot.
Sub CopyData2()
Dim rCell As Range
Dim aTrues() As Variant
Dim rRng As Range
Dim lCnt As Long
'Define the range to search
With DHRSheet
Set rRng = .Range(.Cells(1, 9), .Cells(.Rows.Count, 9).End(xlUp))
End With
'resize array to hold all the 'trues'
ReDim aTrues(1 To Application.WorksheetFunction.CountIf(rRng, "True"), 1 To 2)
For Each rCell In rRng.Cells
If rCell.Value = "True" Then
lCnt = lCnt + 1
'store the string from column 2
aTrues(lCnt, 1) = DHRSheet.Name & "$" & rCell.Offset(0, -7).Value
'store the value from column 3
aTrues(lCnt, 2) = rCell.Offset(0, -6).Value
End If
Next rCell
'offset 2 from the bottom row to leave a row of separation
With DataSheet.Cells(DataSheet.Rows.Count, 1).End(xlUp).Offset(2, 0)
'write the stored information at one time
.Resize(UBound(aTrues, 1), UBound(aTrues, 2)).Value = aTrues
End With
End Sub