I am using the IsInArray function to check if a cell address (Row,Column) exists within an array. For some reason, even though the array contains the value it doesn't match. For example if my array is like this:
18, 812, 84, 34, 412, 87, 74
And OldRow & OldCol gets to 34, the function IsInArray doesn't match it and execute Else.
Below is a sample of the code I am using to try and achieve this:
Set c = .Find(1, LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
cellAddress = c.Address
OldRow = Range(cellAddress).Row
OldCol = Range(cellAddress).Column
If IsInArray("OldRow & OldCol", mappedcells) = False Then
oldmappingrow = Application.Match(OldRow, Worksheets(1).Range("r3:r16"), 0)
If Not IsError(oldmappingrow) Then
OldRowMapped = Worksheets(1).Range("r3:r16").Cells(oldmappingrow).Offset(, 1).Value
End If
oldmappingcol = Application.Match(OldCol, Worksheets(1).Range("r3:r16"), 0)
If Not IsError(oldmappingcol) Then
OldColMapped = Worksheets(1).Range("r3:r16").Cells(oldmappingcol).Offset(, 1).Value
End If
If OldCol > OldRow Then
NewCol = WorksheetFunction.Max(OldRowMapped, OldColMapped)
NewRow = WorksheetFunction.Min(OldRowMapped, OldColMapped)
Else
NewRow = WorksheetFunction.Max(OldRowMapped, OldColMapped)
NewCol = WorksheetFunction.Min(OldRowMapped, OldColMapped)
End If
.Cells(NewRow, NewCol) = .Cells(OldRow, OldCol).Value
.Cells(OldRow, OldCol).Value = "0"
ReDim Preserve mappedcells(UBound(mappedcells) + 1) 'Add next array element
mappedcells(UBound(mappedcells)) = NewRow & NewCol 'Assign the array element
Set c = .FindNext(c)
Debug.Print (OldRow & OldCol & " moved to " & NewRow & NewCol)
Else
Set c = .FindNext(c)
End If
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
And the IsInArray function I am using:
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = Not IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Sorry if the code is messy and could be written cleaner, I am totally new to vba and programming as a whole. Any help would be much appreciated!
"3" & "4" <> 34 but "3" & "4" = "34" and Int("3" & "4") = 34. Try it as,
If not IsInArray(clng(OldRow & OldCol), mappedcells) Then
As noted in the comment from Scott Craner below, go through your code and make sure you are comparing numbers to numbers or text to text and not numbers to text-that-looks-like-a-number.
Related
I am a novice programmer and I'm building a form via VBA for excel where the user will input employee's time sheet and their initials via 16 text box's in the form. The text boxes data are stored to a string array. The code is:
Dim initials(15) As String
initials(0) = TB_Initials_1
initials(1) = TB_Initials_2
initials(2) = TB_Initials_3
...
initials(15) = TB_Initials_15
After using the find function and referencing some data from a one excel sheet, I use
ActiveCell.Offset(0, 2).Value = Join(initials, ".")
to output the following
"js.rs.............." to the active cell in a different excel sheet, (I only entered 2 of the 16 input boxes, hence there's two initials. JS.RS
The trailing .............. is what I want to remove. this will be imported into a Database later via the excel sheet.
How can I remove the xtras ".........'s at the end of the string? I have tried the "Trim()" function, but that does not work in my case. Everything i've tried online does not seem to work either or is referencing items from a work book, not a text box.
Any help is appreciated.
The entire code is below:
Option Explicit
'Variable declaration
Dim startTime(15), endTime(15), ST_Finish_Date As Date
Dim totalmin(15), Total_min, Total_Cost, Rate(15), Line_cost(15), Cost_Per_Part As String
Dim initials(15) As String
Dim i, ii As Integer
Dim Found_ini(15) As Range
Dim Found As Range 'returned value from find
Dim TBtraveller_value As String 'text box traveller value
Dim Found2 As Range 'store part code range
Dim TBDESC As Range ' Returned value from 2nd search
Dim BL_Find_Check As Boolean
Private Sub CB_Write_Click()
create_csv
End Sub
Private Sub Close_Form_Click()
Unload Traveller_Entry
End Sub
'still need to make this for every start / stop time text box.
Private Sub TB_Time_Start_1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
Dim myvar As String
If Not Me.TB_Time_Start_1 Like "??:??" Then
MsgBox "Please use format 'HH:MM'"
Cancel = True
Exit Sub
End If
myvar = Format(Me.TB_Time_Start_1, "hh:mm")
Me.TB_Time_Start_1 = myvar
End Sub
Public Sub travellerNUM_TextBox_Exit(ByVal Cancel As MSForms.ReturnBoolean)
Workbooks("Traveller entryxlsm.xlsm").Activate
TBtraveller_value = travellerNUM_TextBox.Value
If TBtraveller_value = "" Then
MsgBox ("Enter a Shop Traveller Number!")
Exit Sub
Else
TBtraveller_value = travellerNUM_TextBox.Value
Set Found = Sheets("woss").Range("A:A").Find(what:=TBtraveller_value, lookat:=xlWhole)
If Found Is Nothing Then
MsgBox (TBtraveller_value & " Not Found!")
Exit Sub
Else
Part_Code_BOX.Value = Found.Offset(0, 1) 'enters the info into the Part Code Box.
Set Found2 = Found.Offset(0, 1)
End If
If Part_Code_BOX = "" Then
MsgBox ("Traveller number " & TBtraveller_value & " has no part code associated with it." & vbCrLf & "Check Work Order Spread Sheet is FULLY Complete.")
BL_Find_Check = True
Exit Sub
End If
Set TBDESC = Sheets("ProductList").Range("B:B").Find(what:=Found2, lookat:=xlPart)
If TBDESC Is Nothing Then
MsgBox (" Dscription Not Found!")
Else
Desc_Box = TBDESC.Offset(0, 1) 'enters the description into the description Box.
FinishDate_Box = Found.Offset(0, 8) 'enters the finish date into the finish date Box.
Employee = Found.Offset(0, 2) 'enters the Employee name into the employee name Box.
End If
End If
End Sub
Public Sub CB_POST_Click()
On Error Resume Next
startTime(0) = TB_Time_Start_1.Value
startTime(1) = TB_Time_Start_2.Value
startTime(2) = TB_Time_Start_3.Value
startTime(3) = TB_Time_Start_4.Value
startTime(4) = TB_Time_Start_5.Value
startTime(5) = TB_Time_Start_6.Value
startTime(6) = TB_Time_Start_7.Value
startTime(7) = TB_Time_Start_8.Value
startTime(8) = TB_Time_Start_9.Value
startTime(9) = TB_Time_Start_10.Value
startTime(10) = TB_Time_Start_11.Value
startTime(11) = TB_Time_Start_12.Value
startTime(12) = TB_Time_Start_13.Value
startTime(13) = TB_Time_Start_14.Value
startTime(14) = TB_Time_Start_15.Value
startTime(15) = TB_Time_Start_16.Value
endTime(0) = TB_Time_Stop_1.Value
endTime(1) = TB_Time_Stop_2.Value
endTime(2) = TB_Time_Stop_3.Value
endTime(3) = TB_Time_Stop_4.Value
endTime(4) = TB_Time_Stop_5.Value
endTime(5) = TB_Time_Stop_6.Value
endTime(6) = TB_Time_Stop_7.Value
endTime(7) = TB_Time_Stop_8.Value
endTime(8) = TB_Time_Stop_9.Value
endTime(9) = TB_Time_Stop_10.Value
endTime(10) = TB_Time_Stop_11.Value
endTime(11) = TB_Time_Stop_12.Value
endTime(12) = TB_Time_Stop_13.Value
endTime(13) = TB_Time_Stop_14.Value
endTime(14) = TB_Time_Stop_15.Value
endTime(15) = TB_Time_Stop_16.Value
initials(0) = TB_Initials_1
initials(1) = TB_Initials_2
initials(2) = TB_Initials_3
initials(3) = TB_Initials_4
initials(4) = TB_Initials_5
initials(5) = TB_Initials_6
initials(6) = TB_Initials_7
initials(7) = TB_Initials_8
initials(8) = TB_Initials_9
initials(9) = TB_Initials_10
initials(10) = TB_Initials_11
initials(11) = TB_Initials_12
initials(12) = TB_Initials_13
initials(13) = TB_Initials_14
initials(14) = TB_Initials_15
initials(15) = TB_Initials_16
For i = LBound(initials) To UBound(initials)
Set Found_ini(i) = Sheets("rate").Range("B:B").Find(what:=initials(i), lookat:=xlWhole)
Rate(i) = Found_ini(i).Offset(0, 1) 'finds rate for given initials
totalmin(i) = DateDiff("N", startTime(i), endTime(i))
If Found_ini(i) Is Nothing Then
MsgBox (initials(i) & " Not Found! Update Employee Database.")
Exit Sub
'If IsEmpty(Found_ini(i)) = False And IsEmpty(startTime(i)) = True And IsEmpty(endTime(i)) = True Then
'MsgBox "Enter Some Initials, None Found"
Exit Sub
End If
Next
For ii = LBound(totalmin) To UBound(totalmin)
Line_cost(ii) = totalmin(ii) / 60 * Rate(ii)
Next
Total_min = Application.WorksheetFunction.Sum(totalmin)
Total_Cost = Application.WorksheetFunction.Sum(Line_cost)
Cost_Per_Part = Total_Cost / TextBOX_QTYBUILT
If Total_min = 0 Then
MsgBox (" Enter Some Time!")
ElseIf Total_min < 0 Then
MsgBox ("Time is NEGATIVE. Check Entered Times.")
End If
If BL_Find_Check = False Then
MsgBox "The number of minutes between two Times : " & Total_min & vbNewLine & "total cost: " & Total_Cost _
& vbNewLine & "cost Per Part " & Cost_Per_Part, vbInformation, "Minutes Between Two Times"
Sheets("test").Select
Range("A1048576").Select
ActiveCell.End(xlUp).Select
ActiveCell.Offset(1, 0).Select
ActiveCell.Offset(0, 0).Value = FinishDate_Box 'Traveller finish Date
ActiveCell.Offset(0, 1).Value = TBtraveller_value 'Traveller Number
ActiveCell.Offset(0, 2).Value = Join(initials, ".") 'Traveller Employee Given to
ActiveCell.Offset(0, 3).Value = Part_Code_BOX.Value ' part number
ActiveCell.Offset(0, 4).Value = Total_Cost ' traveller total cost
ActiveCell.Offset(0, 5).Value = Cost_Per_Part 'Traveller cost per part
End If
End Sub
Sub create_csv()
Dim FileName As String
Dim PathName As String
Dim ws As Worksheet
Set ws = ActiveWorkbook.Sheets("test")
FileName = "CSV_Output_R1.csv"
PathName = Application.ActiveWorkbook.Path
ws.Copy
ActiveWorkbook.SaveAs FileName:=PathName & "\" & FileName, _
FileFormat:=xlCSV, CreateBackup:=False
End Sub
Thank you,
You can use WorksheetFunction.TextJoin() in Excel2019+ in one string:
ActiveCell.Offset(0, 2).Value = WorksheetFunction.TextJoin(".", True, initials)
A small example for comparison:
Sub test1()
Dim arr(1 To 15)
For i = 1 To 15
arr(i) = IIf(Rnd() > 0.7, "TXT", "")
Next
Debug.Print "With Join(): " & Join(arr, ".")
Debug.Print "With TextJoin(): " & WorksheetFunction.TextJoin(".", True, arr)
End Sub
Output
With Join(): ..TXT........TXT..TXT..
With TextJoin(): TXT.TXT.TXT
Here is a function that I just made to trim empty elements off the end of your array:
Function TrimArray(ByRef StringArray() As String) As String()
'This function removes trailing empty elements from arrays
'Searching from the last element backwards until a non-blank is found
Dim i As Long
For i = UBound(StringArray) To LBound(StringArray) Step -1
If StringArray(i) <> "" Then Exit For
Next i
If i < LBound(StringArray) Then i = LBound(StringArray)
'Creating an array with the correct size to hold the non-blank elements
Dim OutArr() As String
OutArr = StringArray
ReDim Preserve OutArr(LBound(StringArray) To i)
TrimArray = OutArr
End Function
You would use it like so:
Dim Output() As String
Output = TrimArray(initials)
MsgBox Join(Output, ".") & "."
You could build it like this instead of using Join():
ActiveCell.Offset(0, 2).Value = initials(0)
For Counter = 1 To 15
If initials(Counter) <> "" Then
ActiveCell.Offset(0, 2).Value = ActiveCell.Offset(0, 2).Value + "." + initials(Counter)
End If
Next Counter
I am using a VBA script to find the 1's in a 15x15 array. The script finds the row,column address of each cell containing a 1 and maps it to a new (row,column) address based on a mapping table. Below is the excel file:
And this is the script I'm using to perform this mapping action:
Option Explicit
Sub findvalues()
Dim OldRow As Long, OldCol As Long, NewCol As Long, NewRow As Long, OldRowMapped As Long, OldColMapped As Long
Dim oldmappingrow As Variant, oldmappingcol As Variant, c As Range, firstAddress As String, cellAddress As String
With Worksheets(1).Range("a1:o15")
Set c = .Find(1, LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
cellAddress = c.Address
OldRow = Range(cellAddress).Row
OldCol = Range(cellAddress).Column
oldmappingrow = Application.Match(OldRow, Worksheets(1).Range("r3:r16"), 0)
If Not IsError(oldmappingrow) Then
OldRowMapped = Worksheets(1).Range("r3:r16").Cells(oldmappingrow).Offset(, 1).Value
End If
oldmappingcol = Application.Match(OldCol, Worksheets(1).Range("r3:r16"), 0)
If Not IsError(oldmappingcol) Then
OldColMapped = Worksheets(1).Range("r3:r16").Cells(oldmappingcol).Offset(, 1).Value
End If
If OldCol > OldRow Then
NewCol = WorksheetFunction.Max(OldRowMapped, OldColMapped)
NewRow = WorksheetFunction.Min(OldRowMapped, OldColMapped)
Else
NewRow = WorksheetFunction.Max(OldRowMapped, OldColMapped)
NewCol = WorksheetFunction.Min(OldRowMapped, OldColMapped)
End If
.Cells(NewRow, NewCol) = .Cells(OldRow, OldCol).Value
.Cells(OldRow, OldCol).Value = "0"
Set c = .FindNext(c)
MsgBox (OldRow & OldCol & " moved to " & NewRow & NewCol)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
End Sub
I need to prevent the .FindNext() from mapping values which have already been mapped before.
I thought a good idea would be to store each of the NewRow, New Col values in an array and then each loop check that OldRow, OldCol does not equal any of the entries in the NewRow, NewCol array.
How do I create an array to store the mapped cell addresses and check against them each loop?
I have the code below but I know it could be sped up by putting the data into an array which I don't know how. I'd appreciate any help.
Thanks In advance
For Each rngReportCell In rngReport
If rngReportCell = "" Then Exit For
If VBA.UCase(rngReportCell.Offset(0, 1).Value) = "X" Then
wkbSOR.Sheets("Dashboard").range("SSSFlag").Value = True
Else
wkbSOR.Sheets("Dashboard").range("SSSFlag").Value = False
End If
If rngRetrieveCell.Offset(0, 1).Value <> "" Then _
wkbSOR.Sheets(rngRetrieveCell.Value).range(rngRetrieveCell.Offset(0, 1).Value) _
= "'" & rngReportCell.Value
If rngReportCell.Offset(0, 2) <> "" And gRetrieveCell.Offset(0, 2).Value <> "" Then _
wkbSOR.Sheets(rngRetrieveCell.Value).range(rngRetrieveCell.Offset(0, 2).Value) _
= "'" & rngReportCell.Offset(0, 2).Value
TotalRows = range("Base_" & rngRetrieveCell).Rows.count
TotalCols = range("Base_" & rngRetrieveCell).Columns.count
'Copies values using range.value = range.value
range("A7").Offset(range("A7").CurrentRegion.Rows.count, 0).Resize(TotalRows, TotalCols).Value = _
wkbSOR.Sheets(rngRetrieveCell.Value).range("Base_" & rngRetrieveCell).Value
Next rngReportCell 'Store/Hyperion code
Maybe this will help you:
https://stackoverflow.com/questions/17859531/excel-vba-populate-array-with-range-from-specific-sheet
But essentially you will want to just import the range that you want into a 2-D array and iterate through as so (for example):
'Instantiate variant array
Dim arrValues() As Variant
arrValues = Sheet1.Range("A1:D10")
'Iterate through rows
For i = 1 To 10
'Iterate through columns
For j = 1 To 10
'your code here
Next
Next
I am working on a project, which takes a checksheet that the user creates and fills out
and, when the user runs a macro, creates a new workbook that extrapolates and expands the checksheet data, as shown here
What it does is it goes through each of those number labor codes, and runs down the checksheet for all the applicable items, addending them to the list.
Now...I have this working fine, and run through the basic testing. I save the checksheet as an array and pass it through to the new workbook, filtering and creating the new workbook line-by-line.
I just can't help but think that there's a much easier way to do this, as the way I'm doing it now just doesn't seem to be the simplest and most stable way.
I'm open to sharing my code I have so far, but was wondering if you were given this senario, how you would approach it.
Here is the link to my file: https://www.dropbox.com/s/2gobdx1rcabquew/Checksheet_Template_R3.0%20-%20StkOvrflw.xls
Main module, which checks for errors and corrects formatting:
Option Explicit
Public FamilyName As String
Public ModelName As String
Public TaskArray() As Variant
Public TaskArrayRowCount As Integer
Public TaskArrayColCount As Integer
Sub CreateTemplate()
Application.EnableEvents = False
Application.ScreenUpdating = False
'Main SubModule. Runs Formatting and Template Generation
Dim thisWB As Workbook
Dim TaskArray() As Variant
Dim i As Range
Dim MajMinYesNo As Boolean
Dim OPOYesNo As Boolean
If MsgBox("Are you ready to generate the Template?", vbYesNo, "Ready?") = vbNo Then
Application.EnableEvents = True
Application.ScreenUpdating = True
End
End If
MajMinYesNo = False
OPOYesNo = False
Set thisWB = ActiveWorkbook
FamilyName = thisWB.Names("Family_Name").RefersToRange
ModelName = thisWB.Names("Model_No").RefersToRange
Call CreateArray(thisWB)
'Scans Form_Type Column for "R", "S", or "A-E"
For Each i In Range("CS_FormType")
If i Like "[RS]" Then
MajMinYesNo = True
ElseIf i Like "[A-E]" Then
OPOYesNo = True
End If
Next
'Generates Templates As Needed
If MajMinYesNo Then
If MsgBox("Generate Major/Minor Template?", vbYesNo) = vbYes Then
Call MajorMinor_Generate.GenerateMajorMinor(thisWB)
End If
End If
If OPOYesNo Then
If MsgBox("Generate OPO Template?", vbYesNo) = vbYes Then
Call OPO_Generate.GenerateOPO(thisWB)
End If
End If
Application.EnableEvents = True
Application.ScreenUpdating = True
MsgBox ("DONE!")
End Sub
Sub CreateArray(thisWB As Workbook)
'Checks formatting and creates array TaskArray() with all the checksheet data
With thisWB.Sheets(1)
'Confirms equal number of rows in columns "CS_TaskNo", "CS_FormType", and "CS_Task"
If (Not Range("CS_TaskNo").Rows.count = Range("CS_FormType").Rows.count) _
Or (Not Range("CS_TaskNo").Rows.count = Range("CS_Task").Rows.count) Then
MsgBox ("Task_No, Form_Type, and Task_Desc row count does not match. Please fix and try again")
Application.EnableEvents = True
Application.ScreenUpdating = True
End
End If
Call FormatCheck
Application.Union(Range("CS_Heading"), Range("CS_TaskNo"), Range("CS_FormType"), Range("CS_Task"), Range("CS_LaborCodes"), Range("CS_Checks")).Name = "TaskArray"
TaskArrayRowCount = Range("TaskArray").Rows.count
TaskArrayColCount = Range("TaskArray").Columns.count
ReDim TaskArray(TaskArrayRowCount, TaskArrayColCount)
TaskArray = Range("TaskArray").Value
End With
End Sub
Sub FormatCheck()
'Checks for valid labor codes and Form Types
If (Not CheckFormType()) Or (Not CheckLC()) Then
MsgBox ("Errors found, please check red-highlighted cells")
Application.EnableEvents = True
Application.ScreenUpdating = True
End
End If
End Sub
Function CheckFormType()
'Returns False if there's a bad Form_Type entry in range "CS_FormType", True if all OK
Dim i As Range
Dim ReturnVal As Boolean
ReturnVal = True
For Each i In Range("CS_FormType")
Trim (UCase(i.Value))
If Not (i Like "[ABCDEFRS]") Then
Highlight (Cells(i.Row, i.Column))
ReturnVal = False
End If
Next
CheckFormType = ReturnVal
End Function
Function CheckLC()
'Returns False if there's a bad error code, True if all OK _
Formats labor code ranges to add spaces as needed and checks _
labor codes for proper format (###X or ##X). Skips any labor _
codes starting with "28X"
Dim LaborCode As String
Dim LaborCodeLength As Integer
Dim i As Range
Dim j As Integer
Dim LCCell As Range
Dim LCArray() As String
Dim ReturnVal As Boolean
ReturnVal = True
For Each i In Range("CS_LaborCodes")
Trim (UCase(i.Value))
LaborCode = i.Value
If Not Left(LaborCode, 3) Like "28?" Then
LaborCodeLength = Len(LaborCode)
'If string LaborCode is > 4, safe to assume it is a range of labor codes 123A-123F
Select Case LaborCodeLength
Case Is > 4
'Formats Labor Code Range String by adding spaces if necessary (i.e. 123A-123F to 123A - 123F)
For j = 2 To LaborCodeLength Step 1
If (IsNumeric(Mid(LaborCode, j, 1))) And Not IsNumeric(Mid(LaborCode, j + 1, 1)) And Not (Mid(LaborCode, j + 2, 1) = " ") Then
LaborCode = Left(LaborCode, j + 1) & " " & Mid(LaborCode, j + 2)
ElseIf IsNumeric(Mid(LaborCode, j, 1)) And Not (Mid(LaborCode, j - 1, 1) = " ") And Not IsNumeric(Mid(LaborCode, j - 1, 1)) Then
LaborCode = Left(LaborCode, j - 1) & " " & Mid(LaborCode, j)
End If
Next
i = LaborCode
LCArray = Split(LaborCode, " ")
'confirms the labor codes are valid
If (Not IsLaborCode(LCArray(0))) Or (Not IsLaborCode(LCArray(2))) Or (Not IsLaborCodeRange(LCArray(0), LCArray(2))) Then
Highlight (Cells(i.Row, i.Column))
ReturnVal = False
End If
Case 0 To 4
If Not (IsLaborCode(LaborCode)) Then
Highlight (Cells(i.Row, i.Column))
ReturnVal = False
End If
Case Else
Highlight (Cells(i.Row, i.Column))
ReturnVal = False
End Select
End If
Next
CheckLC = ReturnVal
End Function
Function IsLaborCode(LC As String) As Boolean
'returns True if Labor Code is valid, False if invalid _
Labor Code is valid if it is 2 or 3 numbers followed by a letter _
labor code format : ###X or ##X
If LC Like "###[A-Z]" Or LC Like "##[A-Z]" Then
IsLaborCode = True
Else
IsLaborCode = False
End If
End Function
Function IsLaborCodeRange(LCOne As String, LCTwo As String) As Boolean
'returns True if the LC range is valid, False if invalid. _
checks the numerical values to make sure they match and _
makes sure the letters are ascending
If (StrComp(Left(LCOne, Len(LCOne) - 1), Left(LCTwo, Len(LCTwo) - 1)) = 0) And LCOne < LCTwo Then
IsLaborCodeRange = True
Else
IsLaborCodeRange = False
End If
End Function
And here is the other module which actually takes the array and creates the new workbook:
Sub GenerateMajorMinor(thisWB As Workbook)
Dim newWB As Workbook
Dim MajMinArray() As Variant
Set newWB = Workbooks.Add
With newWB
Call FormatWorkbook
Call CreateMajMinArray(newWB, MajMinArray)
Call PopulateItemMaster(MajMinArray)
Call PopulateLaborLink(MajMinArray)
Call SaveFile(newWB, thisWB)
End With
End Sub
Sub SaveFile(newWB As Workbook, thisWB As Workbook)
'saves new workbook into the same file path as the checksheet
Dim i As Integer
Dim FileSavePath As String
Dim FamNameSave As String
FamNameSave = Replace(FamilyName, "/", "_")
i = 1
FileSavePath = thisWB.Path + "/Template (Minor and Major)_" + FamNameSave + ".xls"
a: If Dir(FileSavePath) <> "" Then
FileSavePath = thisWB.Path + "/Template (Minor and Major)_" + FamNameSave + "(" + CStr(i) + ").xls"
i = i + 1
GoTo a:
End If
newWB.SaveAs FileSavePath, FileFormat:=56
End Sub
Sub FormatWorkbook()
'Names and formats sheets
Sheets(1).Name = "Item_Master"
Sheets(2).Name = "Labor_Link"
With Sheets(1)
.Range("A1") = "Company_No"
.Range("B1") = "Family_Name"
.Range("C1") = "Form_Type"
.Range("D1") = "Record_Status"
.Range("E1") = "Task_Desc"
.Range("F1") = "Task_No"
.Range("G1") = "Task_Seq"
.Range("H1") = "Is_Parametric"
End With
With Sheets(2)
.Range("A1") = "Company_Name"
.Range("B1") = "Family_Name"
.Range("C1") = "Form_Type"
.Range("D1") = "Labor_Code"
.Range("E1") = "Print_Control"
.Range("F1") = "Record_Status"
.Range("G1") = "Task_No"
End With
End Sub
Sub CreateMajMinArray(newWB As Workbook, MajMinArray As Variant)
'creates array, removing any OPO/BTS labor codes
With Sheets(3)
Application.EnableEvents = True
Application.ScreenUpdating = True
Dim rng As Range
Set rng = .Range(.Range("A1"), .Cells(TaskArrayRowCount, TaskArrayColCount))
rng = TaskArray
For i = 1 To .Range("A1").End(xlDown).Row Step 1
If .Cells(i, 2) Like "[A-E]" Then
.Rows(i).Delete
i = i - 1
End If
Next
For i = 1 To .Range("A1").End(xlToRight).Column Step 1
If Left(.Cells(1, i), 3) Like "28E" Then
.Columns(i).Delete
i = i - 1
End If
Next
ReDim MajMinArray(.Range("A1").End(xlDown).Row, .Range("A1").End(xlToRight).Column)
MajMinArray = .Range(.Range("A1"), .Cells(.Range("A1").End(xlDown).Row, .Range("A1").End(xlToRight).Column)).Value
.Cells.Clear
End With
End Sub
Sub PopulateItemMaster(MajMinArray As Variant)
With Sheets(1)
'Populates "Item_Master" Sheet
For i = 2 To UBound(MajMinArray) Step 1
.Cells(i, 2) = FamilyName
.Cells(i, 3) = MajMinArray(i, 2)
.Cells(i, 4) = "1"
.Cells(i, 5) = MajMinArray(i, 3)
.Cells(i, 6) = MajMinArray(i, 1)
.Cells(i, 7) = MajMinArray(i, 1)
Next
End With
End Sub
Sub PopulateLaborLink(MajMinArray As Variant)
Dim i As Integer
Dim LaborCode As String
Dim RowCount As Long
Dim LCArray() As String
Dim LastLetter As String
Dim LastFormType As String
'Initializes RowCount and PrintControl
RowCount = 2
PrintControl = 10
With Sheets(2)
For i = 4 To UBound(MajMinArray, 2) Step 1
LaborCode = Trim(MajMinArray(1, i))
'If Labor Code String length is > 4, safe to assume that it is a range of labor codes
Select Case Len(LaborCode)
Case Is > 4
LCArray = Split(LaborCode, " ")
'checks to see if LCArray(0) and LCArray(2) has values
If LCArray(0) = "" Or LCArray(2) = "" Then
MsgBox ("Error with Labor Code range. Please check and re-run")
Application.EnableEvents = True
Application.ScreenUpdating = True
End
End If
LastLetter = Chr(Asc(Right$(LCArray(2), 1)) + 1)
LCArray(2) = Replace(LCArray(2), Right$(LCArray(2), 1), LastLetter)
Do
Call PrintLaborLinkLines(MajMinArray, LCArray(0), RowCount, i)
LastLetter = Chr(Asc(Right$(LCArray(0), 1)) + 1)
LCArray(0) = Replace(LCArray(0), Right$(LCArray(0), 1), LastLetter)
Loop Until LCArray(0) = LCArray(2)
Erase LCArray()
Case Is <= 4
Call PrintLaborLinkLines(MajMinArray, LaborCode, RowCount, i)
End Select
Next
End With
End Sub
Sub PrintLaborLinkLines(MajMinArray As Variant, LaborCode As String, RowCount As Long, i As Integer)
Dim PrintControl As Long
PrintControl = 10
With Sheets(2)
For x = 2 To UBound(MajMinArray) Step 1
If UCase(MajMinArray(x, i)) = "Y" Then
If LastFormType <> MajMinArray(x, 2) Then
PrintControl = 10
End If
.Cells(RowCount, 2) = FamilyName
.Cells(RowCount, 3) = MajMinArray(x, 2)
.Cells(RowCount, 4) = LaborCode
.Cells(RowCount, 5) = PrintControl
.Cells(RowCount, 6) = "1"
.Cells(RowCount, 7) = MajMinArray(x, 1)
RowCount = RowCount + 1
PrintControl = PrintControl + 10
LastFormType = MajMinArray(x, 2)
End If
Next
End With
End Sub
If restructuring the order of the data on the new sheet is possible it seems as though you could copy only visible cells and then write a simple loop to bring in any data that is not explicit (ie Labor Code).
If I declare a dynamic sized array like this
Dim myArray()
Then how I can get in the code if this array is empty or it contains elements?
I tried with IsArray(myArray) function that give me always True,
otherwise if I try with UBound(myArray) function, I get an error.
Any ideas? thanks in advance,
Max
After declaring the array, you have to initialize it:
Dim myArray()
ReDim myArray(-1)
Then such code will always work:
If UBound(myArray)<0 Then
'array is empty....
Else
'array not empty....
End If
Edit: as you can't initialize the array, here is longer way to check if it's empty or not:
Dim x, myCount
myCount = 0
If IsArray(myArray) Then
For Each x In myArray
myCount = myCount + 1
Next
End If
If myCount=0 Then
'array is empty....
Else
'array not empty....
End If
First some notes.
Using Dim A() is not so practical in VBScript, better use ReDim
A(n).
For example ReDim A(-1) is also empty array (no elements) but initialized.
And as the best way coders to talk is by examples...
Dim a(), b(0), c
c = Array(a, b)
ReDim d(-1)
WScript.Echo "Testing HasBound:"
WScript.Echo "a = " & HasBound(a) & ",", _
"b = " & HasBound(b) & ",", _
"c = " & HasBound(c) & ",", _
"d = " & HasBound(d)
WScript.Echo "Testing HasItems:"
WScript.Echo "a = " & HasItems(a) & ",", _
"b = " & HasItems(b) & ",", _
"c = " & HasItems(c) & ",", _
"d = " & HasItems(d)
'> Testing HasBound:
'> a = False, b = True, c = True, d = True
'> Testing HasItems:
'> a = False, b = True, c = True, d = False
Function HasBound(anyArray)
On Error Resume Next
HasBound = UBound(anyArray)
HasBound = (0 = Err)
On Error Goto 0
End Function
Function HasItems(anyArray)
For Each HasItems In anyArray
HasItems = 1
Exit For
Next
HasItems = (HasItems > 0)
End Function
As you see, 2 functions with different purpose. The difference is visible on array d which "has-boundary" but "has-not-items".
I found a solution, I wrote a specific function to check if an array is null or not; the function doesn't check if it has elements inside but only if the array is declared as dynamic without dimensions and no elements.
Dim dynamic_array() 'array without a dimension
Dim empty_array(0) 'array with a dimension but without an element inside
Dim full_array(0) : full_array(0) = "max" 'array with a dimension and with an element inside
Function IsNullArray(input_array)
On Error Resume Next
Dim is_null : is_null = UBound(input_array)
If Err.Number = 0 Then
is_null = False
Else
is_null = True
End If
IsNullArray = is_null
End Function
If IsNullArray(dynamic_array) Then
Response.Write("<p>dynamic array not 'ReDimed'</p>")
End If
If Not IsNullArray(empty_array) Then
Response.Write("<p>" & UBound(empty_array) & "</p>") 'return the last index of the array
End If
If Not IsNullArray(full_array) Then
Response.Write("<p>" & full_array(UBound(full_array)) & "</p>") 'return the value of the last element of the array
End If
The one thing I can think of right now is:
On Error resume next
if UBound(myArray) < 0 then response.write "Empty array" end if
EDIT: Max's comment
I've always checked for UBound = 0 and the first element is empty too:
If UBound(myArray) = 0 Then
if myArray(0) = "" then ''Depending on the type of the array
''array is empty....
End If
End If