Storing a String and ouputting to Multiple Cells in VBA - arrays

I currently have code set up that will loop through all the worksheets in my workbook, paste a date in a cell which, when non blank, will have the remaining cells in the row populate with data.
At the beginning of each row - I have a formula that will say "Error" if any of the cells in that row has an error in it. like this:
I then have another loop which will go back through each worksheet and check to see if there is an error in that cell and if so, will go to the first sheet in the workbook to a specific cell and add "Error on xyz Tab". If there are multiple errors, it'll go to the next row down and paste it. So it looks like this:
I'm thinking instead of looping through each sheet again, could i store the text string in a variable/array and just paste it on the front sheet at the end of the loop in the same manner?
This is the code for the error loop that's currently set up:
For I = 1 To WS_Count
ActiveWorkbook.Worksheets(I).Activate
Cells.Find(What:="Date", After:=ActiveCell, LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).End(xlDown).Offset(0, -1).Activate
If ActiveCell.Value = "Error" Then
Application.Goto "ErrorCheck"
If ActiveCell.Offset(1, 0).Value = vbNullString Then
ActiveCell.Offset(1, 0).Value = "Error on " & ActiveWorkbook.Worksheets(I).Name & " " & Hour(Now) & "00"
Else
Selection.End(xlDown).Activate
ActiveCell.Offset(1, 0).Value = "Error on " & ActiveWorkbook.Worksheets(I).Name & " " & Hour(Now) & "00"
End If
Else
End If
Next I

So with this I personally wouldn't want to use an array. I would prefer using a collection. It is easier because you do not know the parameters for your array so it is tough to give it dimensions.
Nonetheless find below a possible solution. Work it to your needs. I have yet to test or debug myself. But should do the trick.
Sub ErrorCheck()
Dim x As Long, lRow1 As Long, lRow2 As Long
Dim myCollection As New Collection
Dim ws As Worksheet
Dim mySheet As Worksheet
Set mySheet = Sheets("ErrorCheckSheet")
'create the for loop to cycle through worksheets
For Each ws In ThisWorkbook.Worksheets
'set the lrow to iterate through column
'set the colum for your need - "Error" column
lRow1 = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
'IF lRow does not match your cell, use a static variable ie. 50
'assuming your data starts in row 2 as per picture
For x = 2 To lRow1
'check each cell for error text
If ws.Range("A" & x).Text = "Error" Then
'when found add to collection
'adjust to meet your cell you want to input into collection
myCollection.Add ws.Range("B" & x).Text
End If
Next x
Next ws
'once you have completely cycled through your workbook your collection will now be loaded
For x = 1 To myCollection.Count
'set the lrow on the sheet you want to enter the data in
lRow2 = mySheet.Range("U" & mySheet.Rows.Count).End(xlUp).Row + 1
'now set the variable
mySheet.Range("U" & lRow2).Value = "Error on" & myCollection(x)
Next x
Set myCollection = New Collection
Set mySheet = Nothing
End Sub

Related

Delete multiple columns from Multiple sheets

I am trying to delete Multiple columns from Multiple sheets while retaining those found in a list.
For example I have sheet1, sheet2, sheet3, ..., sheet7.
From these sheets I have particular columns to be keep like.
From sheet1 I want keep columns like s.no, cust.name, product, date remaining all should be deleted same from sheet2 I want to keep prod.disc,address, pin remaining all should be deleted like I have remaining sheets in that I want to keep particular columns remaining all should be deleted.
I am trying to do using arrays but not able start how to do. I have basic syntax.
Sub sbVBS_To_Delete_Specific_Multiple_Columns()
Sheets("Sheet1").Range("A:A,C:C,H:H,K:O,Q:U").EntireColumn.Delete
End Sub`[code]
But that didn't work for me because in future some columns may add in it and I want columns should recognize with name which column to keep and remaining to discard.
OK, here is the basic code. Specify the worksheet and the columns to be deleted in the main procedure. Set the row in which to find the captions in the sub-procedure.
Sub DeleteColumns()
' 17 Mar 2017
Dim ClmCaption As Variant
Dim Ws As Worksheet
Dim i As Integer
Set Ws = ActiveSheet
' better to specify the sheet by name, like Set Ws = ThisWorkbook.Worksheets("My Excel")
Application.ScreenUpdating = False ' freeze screen (speeds up execution)
ClmCaption = Array("One", "two", "three", "four", "five")
' specify all the columns you want to delete by caption , not case sensitive
For i = 0 To UBound(ClmCaption) ' loop through all the captions
DelColumn Ws, CStr(ClmCaption(i)) ' call the sub for each caption
Next i
Application.ScreenUpdating = True ' update screen
End Sub
Private Sub DelColumn(Ws As Worksheet, Cap As String)
' 17 Mar 2017
Dim CapRow As Long
Dim Fnd As Range
CapRow = 3 ' this is the row where the captions are
Set Fnd = Ws.Rows(CapRow).Find(Cap) ' find the caption
If Fnd Is Nothing Then
MsgBox "The caption """ & Cap & """ doesn't exist." & vbCr & _
"The column wasn't deleted.", _
vbInformation, "Invalid parameter"
Else
Ws.Columns(Fnd.Column).EntireColumn.Delete Shift:=xlToLeft
End If
End Sub
You can run the code as it is but you will get a lot of error messages because the specified captions don't exist.
The following uses a Scripting Dictionary object that maintains a list of worksheets to be processed as the dictionary keys with an array of column header labels to keep as the associated items.
Option Explicit
Sub delColumnsNotInDictionary()
Dim d As Long, ky As Variant, dict As Object
Dim c As Long, lc As Long
Set dict = CreateObject("Scripting.Dictionary")
dict.comparemode = vbTextCompare
dict.Item("Sheet1") = Array("s.no", "cust.name", "product", "date")
dict.Item("Sheet2") = Array("prod.disc", "address", "pin")
dict.Item("Sheet50") = Array("foo", "bar")
With ThisWorkbook
For Each ky In dict.keys
With Worksheets(ky)
lc = .Cells.Find(What:="*", After:=.Cells(1, 1), LookIn:=xlFormulas, LookAt:=xlPart, _
SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, _
MatchCase:=False, SearchFormat:=False).Column
For c = lc To 1 Step -1
'filter array method of 'not found in array'
'WARNING! CASE SENSITIVE SEARCH - foo <> FOO
If UBound(Filter(dict.Item(ky), .Cells(1, c).Value2)) = -1 Then
'.Cells(1, c).EntireColumn.Delete
Else
Debug.Print .Cells(1, c).Value2 & " at " & _
UBound(Filter(dict.Item(ky), .Cells(1, c).Value2))
End If
'worksheet MATCH method of 'not found in array'
'Case insensitive search - foo == FOO
If IsError(Application.Match(.Cells(1, c).Value2, dict.Item(ky), 0)) Then
.Cells(1, c).EntireColumn.Delete
Else
Debug.Print .Cells(1, c).Value2 & " at " & _
Application.Match(.Cells(1, c).Value2, dict.Item(ky), 0)
End If
Next c
End With
Next ky
End With
dict.RemoveAll: Set dict = Nothing
End Sub
Note that I have included two methods for determining whether a column header label is within the array of columns to keep. One is case-sensitive (the array Filter method) and the other is not (worksheet function MATCH method). The case-insensitive search method is currently active.

copy and paste specific cell(using .find) in worksheet array using vba

The code below selects tabs based on the color of the tab. Each sheet is formatted the same, they just contain different values. I am trying to using .find and offset to find a particular cell (it corresponds with current fiscal week plus one) and then copy and paste that cell as values instead of formulas. The code below selects the tabs needed and locates the correct cell but does not copy and paste that cell as values. I am trying to not name sheets specifically because this code will be used on multiple workbooks all with different tab names.
Sub freeze()
Dim ws As Worksheet
Dim strg() As String
Dim count As Integer
count = 1
For Each ws In Worksheets
If ws.Tab.Color = 255 Then
ReDim Preserve strg(count) As String
strg(count) = ws.Name
count = count + 1
Else
End If
Next ws
Sheets(strg(1)).Select
Dim aCell As Range
Set aCell = Range("B9:B79").Find(What:=Worksheets("EmailTemplate").Range("A1").Value)
If Not aCell Is Nothing Then
Sheets(strg(1)).aCell.Select
ActiveCell.Offset(0, 6).Select
Selection.copy
Selection.PasteSpecial xlPasteValues
Else
End If
For I = 2 To UBound(strg)
Sheets(strg(I)).Select False
Next I
End Sub
Thank you
Update #2 (Sun. 11:15 EDT) Added debug statements to assist you; Needed to add reference to 'ActiveSheet' in the 'Find' Code will loop thru all 'Red' sheets, find a match (if any) and copy/paste values.
Debug code will show Red tab names, search value, results, formula, value
Option Explicit
Sub freeze()
Dim ws As Worksheet
Dim aCell As Range
Dim strg() As String
Dim count As Integer
Dim i As Integer
count = 0
' Get each RED sheet
For Each ws In Worksheets
If ws.Tab.Color = 255 Then ' Find only RED tabs
Debug.Print "-----------------------------------------------------------------------"
Debug.Print "Name of Red Sheet: '" & ws.Name & "'" ' Debug...
'ReDim Preserve strg(count + 1) As String
'count = count + 1 ' This code not necessary as you can just reference the ws.name
'strg(count) = ws.Name ' Ditto
Sheets(ws.Name).Select
Set aCell = ActiveSheet.Range("B9:B79").Find(What:=Worksheets("EmailTemplate").Range("A1").value)
If Not aCell Is Nothing Then
ActiveSheet.Cells(aCell.Row, aCell.column).Select
ActiveCell.Offset(0, 6).Select ' Offset same row, + 6 columns
Debug.Print "Found Match for '" & Worksheets("EmailTemplate").Range("A1").value & _
"' in: R" & aCell.Row & ":C" & aCell.column & vbTab & "Formula: '" & ActiveCell.Formula & "'; Value: '" & ActiveCell.value & "'"
' Weird, but was unable to use 'aCell.Select' 2nd time thru loop
Selection.Copy
Selection.PasteSpecial xlPasteValues
Else
Debug.Print "Did NOT find a match for: '" & Worksheets("EmailTemplate").Range("A1").value & "' in sheet '" & ws.Name & "'"
End If
Application.CutCopyMode = False ' Unselect cell
End If
Next ws
End Sub
You can't do this:
Sheets(strg(1)).aCell.Select
The sheet is already stored in the range object aCell. You also shouldn't use select and pasting the value is not necessary. Here is what I would do:
Dim aCell As Range
Set aCell = Sheets(strg(1)).Range("B9:B79").Find(What:=Worksheets("EmailTemplate").Range("A1").Value)
If Not aCell Is Nothing Then
aCell.Offset(0, 6).Value = aCell.Offset(0, 6).Value
End If
I don't understand what you want to achieve with the second loop. .Select doesn't accept arguments I think?
edit: actually .Select does accept the replace option if applied to worksheets to extend the current selection, sorry about that!

Searching through values in dynamic array (vba)

Users select desired options through use of a checkbox. The caption values of each check box are stored in a dynamic array then displayed in message box confirming the selections.
I now need to loop through a range of cells, at every row determining if cell (x,4) is equal to any value in the array, but I don't know how to loop like that. See code below where the array is populated.
Thank you in advance!
Sub ProcessStrats_Click()
Dim ctl As Control
Dim cnt As Long
Dim msg As String
Dim i As Long
Dim cResp As Integer
Dim stArray() As Variant
cnt = 0 'Initialize counter outside of loop
For Each ctl In StratFill.Controls 'look at every control in StratForm box
If Left(ctl.Name, 8) = "CheckBox" Then 'if the control is named 'checkbox' then
If ctl.Value = True Then 'if the control is marked as 'true' i.e. checked, then
ReDim Preserve stArray(0 To cnt) 'Reset the array dimension on each iteration of loop
stArray(cnt) = ctl.Caption 'Add value in value of checkbox caption to Array
cnt = cnt + 1 'Advance the counter to next array item
End If
End If
Next
Unload StratFill 'unload and close stratfill form
msg = "The following strategies will be priced:" & vbNewLine & vbNewLine
For i = LBound(stArray) To UBound(stArray) 'loops through all values of array
msg = msg & stArray(i) & vbCR 'strings together displayed txt
Next i
If MsgBox(msg, vbYesNo, "Confirm Strategies") = vbYes Then
'if yes is clicked
Call RunPricing '"RunPricing" will run
Else 'if no is clicked
StratFill.Show 'then the strategy selector box will display again
End If
End Sub
Try this:
For i = 1 To UBound(stArray) 'loops through all values of array
Range("$D2:" & Range("D" & Rows.Count).End(xlUp).Address).Select
Selection.Find(What:=stArray(i), After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Offset(0, 2).Select
msg = msg & stArray(i) & ActiveCell.Value & vbCR 'strings together displayed txt
Next i

Dynamic Sheets(Array())

I want to select a array of sheets using the Sheets(Array()) method.
The sheets I want to select are named in the cells of my workheet Printlist.
The sheetnames are listed form column D to K.
Not all cells are filled so if I use the folowing function it errors on the rows with blank cells. How can I avoid this error:
This is what the sheet looks like:
And this is the code
Sub PDF_maken()
Dim ws As Worksheet
Dim LR As Long
Dim r As Range
Dim Mypath As String
Dim strarray As String
Set ws = ActiveWorkbook.Worksheets("Printlijst")
LR = ws.Cells(Rows.Count, 1).End(xlUp).Row
For Each r In ws.Range("B20:B20").Cells
If Not IsEmpty("B" & r.Row) Then
Mypath = ws.Range("B" & r.Row).Text
colCheck = 4
Do Until Cells(r.Row, colCheck) = ""
strarray = strarray & IIf(colCheck > 4, ",") & """" & Cells(r.Row, colCheck).Value & """"
colCheck = colCheck + 1
Loop
ActiveWorkbook.Sheets(strarray).Select
ActiveWorkbook.SelectedSheets.ExportAsFixedFormat _
Type:=xlTypePDF, _
Filename:=Mypath & ws.Range("C" & r.Row).Text & ".pdf", _
Quality:=xlQualityStandard, IncludeDocProperties:=True, _
IgnorePrintAreas:=False, OpenAfterPublish:=False
End If
Next r
End Sub
You can use a regular array rather than the Array() function to create the array. Then you can loop through the cells that contains sheet names and only add them if they're not blank. Here's an example.
Sub PDF_maken()
Dim ws As Worksheet
Dim lLastRow As Long
Dim rMap As Range
Dim sPath As String
Dim aSheets() As String
Dim lShCnt As Long
Dim rSh As Range
Set ws = ActiveWorkbook.Worksheets("Printlist")
lLastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
For Each rMap In ws.Range("B2:B" & lLastRow).Cells
'Make sure there's a path
If Not IsEmpty(rMap.Value) Then
sPath = ws.Range("B" & rMap.Row).Text
're-dimension an array to hold all the sheet names
ReDim aSheets(1 To Application.WorksheetFunction.CountA(rMap.Offset(, 2).Resize(1, 8)))
'reset the counter
lShCnt = 0
'loop through all the cells that might have a sheet name
'and add them to the array
For Each rSh In rMap.Offset(, 2).Resize(1, 8).Cells
If Not IsEmpty(rSh.Value) Then
lShCnt = lShCnt + 1
aSheets(lShCnt) = rSh.Text
End If
Next rSh
ActiveWorkbook.Sheets(aSheets).Select
ActiveSheet.ExportAsFixedFormat xlTypePDF, sPath & rMap.Offset(0, 1).Text & ".pdf"
End If
Next rMap
ws.Select
End Sub
If you get Error 9: Subscript Out of Range there are three things to check:
The first one is that you spelled a sheet name wrong. Make sure there are no spaces or other funny business that makes it look like you have a good sheet name and you don't.
Next, make sure you qualify all of your references back to the workbook level. Depending on where your code is, unqualified references can point to different places. Don't ever use Sheets(). Always use ThisWorkbook.Sheets() or some other workbook reference. That will make sure you're not trying to access a sheet in a workbook that you didn't intend to.
Finally, you can get that error if you pass numbers to Sheets because your sheet names are numbers. Or rather they look like numbers, but they're really text. sheets(array(1234,4567)).select is different than sheets(array("1234","4567")).select. You have to pass strings to Sheets or you'll get that error. Kind of. You can pass numbers, but it will Select the sheets based on their index numbers rather than their names. That's why you have to be extra careful when your sheet names look like numbers.
Do a similar loop,
something like
colCheck=4
do until cells(r.row,colCheck)=""
strArray=strarray & iif(colCheck>4,",","") & cells(r.row,colCheck).value
colCheck=colCheck+1
loop
then you'll get something like a,b,c I've not tested this, so may need some tweaking. I'll revisit in a moment.

Insert Cell after unequal value, copy, then delete inserted rows

Hi there I have created the following two macros however it is inserting a row after the last cell with data. I believe this is a result of my loop condition being Do Until ActiveCell.Value = "". I would like to have the loop stop at the last cell with data.
I tried using variables Do Until Loop_Long = LastRow but this did not work for me.
All I would like is to have a macro insert a row between cells with unlike data. Then a macro that will find empty cells in the column,the ones we previously inserted, and then delete the row.
As outlined above the issue is it is inserting an extra row and not deleting it, if you put values all the way down column B after your data in column A you will see what I mean.
Here is my code:
Option Explicit
Sub Macro1()
'Insert Blank Row Between Names
Sheets("Sheet1").Select
Range("A1").Select
Do Until ActiveCell.Value = ""
If ActiveCell.Value <> ActiveCell.Offset(1).Value Then
ActiveCell.Offset(1).EntireRow.Insert
ActiveCell.Offset(1).Select
End If
ActiveCell.Offset(1).Select
Loop
End Sub
Sub Macro2()
Dim LastRow As Long
'Delete Inserted Rows
Sheets("Sheet1").Select
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("A" & LastRow).Select
Do Until ActiveCell.Value = Range("A1")
If ActiveCell.Value <> ActiveCell.Offset(-1).Value Then
ActiveCell.Offset(-1).EntireRow.Delete Shift:=xlUp
ActiveCell.Offset(-1).Select
End If
ActiveCell.Offset(-1).Select
Loop
End Sub
From what you've told me, the below code should work for you (and it better follows best practices)... Have you considered copying the data as is and then inserting the rows once you've pasted the data to the new location? That would cut out a step.
Option Explicit
'Declare module-level variables.
Dim sht As Worksheet
Dim Cell As Range
Dim NameRng As Range
Dim LastRow As Long
Sub test()
'Add blank rows.
Set sht = ActiveWorkbook.Sheets("Sheet1")
LastRow = sht.Range("A" & Rows.count).End(xlUp).Row
Set NameRng = sht.Range("A1:A" & LastRow)
For Each Cell In NameRng
If Cell <> Cell.Offset(1, 0) And Cell <> "" Then
Cell.Offset(1, 0).EntireRow.Insert
End If
Next Cell
End Sub
Sub test2()
'Delete blank rows.
Set sht = ActiveWorkbook.Sheets("Sheet1")
LastRow = sht.Range("A" & Rows.count).End(xlUp).Row
Set NameRng = sht.Range("A1:A" & LastRow + 1)
For Each Cell In NameRng
If Cell = "" Then
Cell.EntireRow.Delete
End If
Next Cell
End Sub

Resources