Object in Variant array disappears when accessed - arrays

Sub test()
Dim myVar(): myVar = getHeadingTOC(ActiveDocument)
myVar(9, 2).Follow '9 is not significant; randomly chosen for test
End Sub
Function getHeadingTOC(oDoc As Document) As Variant
Dim Match, Matches, varArray
Dim item As Long, subItem As Long
Dim rgx As Object: Set rgx = CreateObject("VBScript.RegExp")
Dim ptrn As String: ptrn = "([\d\.]*)\s(.*)"
Dim rng As Range: Set rng = oDoc.Range(0, 0): rng.MoveStart wdStory, 9
Dim toc As TableOfContents
' On Error GoTo housekeeping
With rgx
.Pattern = ptrn
.MultiLine = False
.Global = True
.IgnoreCase = True
End With
Set toc = oDoc.TablesOfContents.Add(Range:=rng, UseHeadingStyles:=True, UpperHeadingLevel:=1, LowerHeadingLevel:=7, IncludePageNumbers:=False, UseHyperlinks:=True)
With toc.Range
ReDim varArray(1 To .Paragraphs.Count, 0 To 2)
For item = 1 To .Paragraphs.Count
With .Paragraphs(item).Range
Set Matches = rgx.Execute(Trim$(.Text))
For Each Match In Matches
For subItem = 0 To Match.Submatches.Count - 1
varArray(item, subItem) = Match.Submatches(subItem)
Next subItem
Set varArray(item, 2) = .Hyperlinks(1)
Next Match
End With
Next item
End With
getHeadingTOC = varArray
housekeeping:
If Not toc Is Nothing Then toc.Delete
Set toc = Nothing
Set Match = Nothing
Set Matches = Nothing
Set rgx = Nothing
Set rng = Nothing
End Function
getHeadingTOC returns a 2-dimensional variant array of the following types:
- myVar(X,0) is a String
- myVar(X,1) is a String
- myVar(X,2) is a Word.Hyperlink object
By Inspection, the array leaves the providing function (getHeadingTOC) containing the desired hyperlink but the array arrives at _test with the hyperlinks deleted and thus fails (5825 error is generated) when attempting to execute the Follow command.
What am I missing about this unexpected behavior?

Related

Convert Comma Separated String to Array for Text Conversion

I am creating an application [In Access] to convert text files to excel files because my company does a lot of them. So I created a table that I keep the File Name, Num of Cols, and a 3rd field with the common separated list of the datatypes for the columns.
Everything is working except I cannot get the comma separated list to work as an array. First, I call the ImportText File:
Call ImportTextFile("TestFileName", 7, ConvertStringToArray(",,,,,,2"))
Then I ConvertSTringToArray:
Function ConvertStringToArray(ByVal StringToConvert As String) As Variant
Dim rawArray() As String
Dim varArray() As Variant
rawArray = Split(StringToConvert, ",")
ReDim varArray(LBound(rawArray) To UBound(rawArray))
Dim i As Long: For i = LBound(rawArray) To UBound(rawArray)
varArray(i) = rawArray(i)
Next i
ConvertStringToArray = varArray
End Function
Then it passes to ImportTextFile (Up until here aDataTypes is passed as an Array.):
Public Sub ImportTextFile(ByVal strFileName As String, ByVal iNumOfCols As Integer, Optional aDataTypes As Variant = Nothing)
On Error GoTo Sub_Err
Dim xl As New Excel.Application: Set xl = New Excel.Application
xl.DisplayAlerts = False
Dim sPathAndFile As String: sPathAndFile = cPath & strFileName
Dim wb As Workbook: Set wb = xl.Workbooks.Add
Dim ws As Worksheet: Set ws = wb.Sheets(1)
With ws.QueryTables.Add(Connection:="TEXT;" & sPathAndFile & ".txt", Destination:=ws.Range("$A$1"))
.FieldNames = True
.RowNumbers = False
.RefreshStyle = xlInsertDeleteCells
.SaveData = True
.TextFilePlatform = 437
.TextFileStartRow = 1
.TextFileParseType = xlDelimited
.TextFileTextQualifier = xlTextQualifierDoubleQuote
.TextFileConsecutiveDelimiter = False
.TextFileTabDelimiter = False
.TextFileSemicolonDelimiter = False
.TextFileCommaDelimiter = True
.TextFileSpaceDelimiter = False
If IsArray(aDataTypes) Then
.TextFileColumnDataTypes = aDataTypes
End If
.TextFileTrailingMinusNumbers = True
.Refresh BackgroundQuery:=False
End With
End Sub
However, it crashes on this line:
.TextFileColumnDataTypes = aDataTypes
What am I missing? Why isn't this working?
The Error message that I receive is:
Invalid Procedure Call or Argument
TextFileColumnDataTypes expects an array of XlColumnDataType values, but you're passing in an array of strings.
Maybe consider reworking your array function:
Function FormatsArray(ByVal StringToConvert As String) As Variant
Dim i As Long
Dim rawArray() As String
Dim varArray As Variant, v
rawArray = Split(StringToConvert, ",")
ReDim varArray(LBound(rawArray) To UBound(rawArray))
For i = LBound(rawArray) To UBound(rawArray)
v = Trim(rawArray(i))
If Len(v) > 0 Then 'specific format supplied?
varArray(i) = CLng(v)
Else
varArray(i) = xlGeneralFormat 'use default
End If
Next i
FormatsArray = varArray
End Function

VBA - WebScraping Get elements for classname for equal classNames

I want to extract two values (numbers and positions) per player which have an equal class name "text". I am currently unable to select the two correct values per player.
My problem is I actually have only the first and the second value in "HTMLnumbers" and "HTMLposition". Otherwise if I select all items for the class "text", the first player gets the value for number and the second player gets the value for position. Thats also not correct.
Option Explicit
Sub erweiterteWerte()
Dim IE As New SHDocVw.InternetExplorer
Dim HTMLdoc As MSHTML.HTMLDocument
Dim HTMLplayerRow As MSHTML.IHTMLElementCollection
Dim i As Integer
Dim j As Integer
Dim HTMLnumbers As Object
Dim HTMLposition As Object
Dim numbers As String
Dim position As String
Dim letzteZeile As Long
Dim aktuelleZeile As Long
IE.Visible = False
IE.Navigate "https://examplexyz.de"
Do While IE.ReadyState <> READYSTATE_COMPLETE
Loop
Application.Wait (Now + TimeValue("0:00:7"))
Set HTMLdoc = IE.Document
Set HTMLplayerRow = HTMLdoc.getElementsByClassName("playerRow")
Set HTMLnumbers = HTMLplayerRow(0).getElementsByClassName("text")
If Not HTMLnumbers Is Nothing Then
numbers = HTMLnumbers.Item(0).innerText
position = HTMLnumbers.Item(1).innerText
Else
numbers = "no_value"
End If
Debug.Print numbers
Debug.Print position
IE.Quit
End Sub
Untested, but to illustrate the basic approach:
Sub erweiterteWerte()
Dim IE As SHDocVw.InternetExplorer
Dim HTMLdoc As MSHTML.HTMLDocument
Dim playerRows As MSHTML.IHTMLElementCollection
Dim playerBadges As MSHTML.IHTMLElementCollection
Dim player As Object, badge As Object
Set IE = New SHDocVw.InternetExplorer
IE.Visible = False
IE.Navigate "https://play.kickbase.com/transfermarkt/kaufen"
Do While IE.ReadyState <> READYSTATE_COMPLETE
Loop
Application.Wait (Now + TimeValue("0:00:07"))
Set HTMLdoc = IE.Document
Set playerRows = HTMLdoc.getElementsByClassName("playerRow")
For Each player In playerRows
Debug.Print "---------------"
Debug.Print classText(player, "firstName") & " " & classText(player, "lastName")
Set playerBadges = player.getElementsByClassName("badge")
For Each badge In playerBadges
Debug.Print badge.innerText
Next badge
Next player
IE.Quit
End Sub
'Helper function to get a child (of `obj`) element's text using its className
' (only handles a single instance but could be extended)
Function classText(obj As Object, classname As String) As String
Dim els As Object
Set els = obj.getElementsByClassName(classname)
If els.Length > 0 Then
classText = els(0).innerText
Else
classText = "[not found]"
End If
End Function

Reusing VBA Array

I would like to reuse vSheetNamesTemp array.
It's collecting sheets with q* but I want to use it for other sheets like w*.
Sub main()
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
Set swDrawing = swModel
Dim vSheetNamesTemp As Variant
vSheetNamesTemp = swDrawing.GetSheetNames
removed = 0
For i = 0 To UBound(vSheetNamesTemp)
vSheetNamesTemp(i - removed) = vSheetNamesTemp(i)
SheetName = vSheetNamesTemp(i)
If Not SheetName Like "q*" Then
removed = removed + 1
End If
Next i
If (UBound(vSheetNamesTemp) - removed) >= 0 Then
ReDim Preserve vSheetNamesTemp(0 To (UBound(vSheetNamesTemp) - removed))
vSheetNames = vSheetNamesTemp
End If
End Sub
Try the next approach, please:
Create a variable on top of the module (declarations side):
Private vSheetNames As Variant
Copy your transformed Sub:
Sub main()
Dim arrCriteria As Variant, El As Variant
'Please, appropriately declare the used variables. I do not use Solidworks
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
Set swDrawing = swModel
vSheetNames = swDrawing.GetSheetNames
arrCriteria = Split("q*,w*,x*", ",") 'use here as many criteria you need
For Each El In arrCriteria
If UBound(vSheetNames) >= 0 Then
removeSh vSheetNames, El
End If
Next
End Sub
In the same module, copy the next function:
Private Function removeSh(vSheetNamesTemp As Variant, strCriteria As String)
Dim removed As Long, i As Long
removed = 0
For i = 0 To UBound(vSheetNamesTemp)
vSheetNamesTemp(i - removed) = vSheetNamesTemp(i)
If Not vSheetNamesTemp(i) Like strCriteria Then
removed = removed + 1
End If
Next i
If (UBound(vSheetNamesTemp) - removed) >= 0 Then
ReDim Preserve vSheetNamesTemp(0 To (UBound(vSheetNamesTemp) - removed))
vSheetNames = vSheetNamesTemp
End If
End Function
The code is not tested, but it should work, I think. Please test it and send some feedback.

get array into new sheet?

I have a function that loads certain data from a dynamic table into an array. The function works fine, when I check the local window I get the correct data. Also when I call the data from a sub, everything seems to work fine till I write the array to a new sheet, then I only get the first record repeatedly.
This is my code:
Function LoadData() As String()
Dim rng2 As Range, intJaNein As Integer, rngZelle As Range, X As Integer, cntAnzahl As Integer
Dim strAusgabe() As String 'R?ckgabe Array
intJaNein = 1
X = 0
Set rng2 = Range("tblMaschinen[DisplayList]")
cntAnzahl = WorksheetFunction.CountIfs(rng, m_intListIndex, rng2, intJaNein)
ReDim strAusgabe(cntAnzahl)
For Each rngZelle In rng2.Cells
If rngZelle.Offset(, -2).value = 0 And _
rngZelle.value = 1 And _
X <= cntAnzahl Then
strAusgabe(X) = rngZelle.Offset(, -1).value
X = X + 1
End If
Next rngZelle
LoadData = strAusgabe
End Function
Sub Test()
Dim sht As Worksheet, rng As Range, arr() As String
If ThisWorkbook.Worksheets("Loeschen") Is Nothing Then
Set sht = ActiveWorkbook.Worksheets.Add
sht.Name = "Loeschen"
End If
Set rng = Range("A1:A19")
arr = cls.LoadData
rng.value = arr
End Sub
This is the locals output when getting to the last row of code (rng.value = arr)
And this is what appears in my worksheet.

VBA function that outputs all unique values on different cells in the same row in worksheet

I'm trying to create a function (that when you pass it an array (Maybe a range is better?) that it outputs all the unique values in the same row on different cells.
I've gotten as far as knowing how to identify the elements (which I don't think I've done right :( ) but I'm not sure how I'd output all the unique values. I only get the first one.
My code is as follows:
Function UniqueItems(ArrayIn, Optional Count As Variant) As Variant
' Accepts an array or range as input
' If Count = True or is missing, the function returns the number of unique elements
' If Count = False, the function returns a variant array of unique elements
Dim Unique() As Variant ' array that holds the unique items
Dim Element As Variant
Dim i As Integer
Dim FoundMatch As Boolean
'If 2nd argument is missing, assign default value
If IsMissing(Count) Then Count = True
' Counter for number of unique elements
NumUnique = 0
' Loop thru the input array
For Each Element In ArrayIn
FoundMatch = False
' Has item been added yet?
For i = 1 To NumUnique
If Element = Unique(i) Then
FoundMatch = True
Exit For '(exit loop)
End If
Next i
AddItem:
'If not in list, add the item to unique list
If Not FoundMatch And Not IsEmpty(Element) Then
NumUnique = NumUnique + 1
ReDim Preserve Unique(NumUnique)
Unique(NumUnique) = Element
End If
Next Element
If Count Then UniqueItems = NumUnique Else UniqueItems = Unique
Something like:
Function UniqueItems(ArrayIn) As Variant
Dim vData As Variant
Dim vNewdata() As Variant
Dim colUniques As Collection
Dim lCt As Long
If TypeName(ArrayIn) = "Range" Then
vData = ArrayIn.Value
Else
vData = ArrayIn
End If
Set colUniques = New Collection
'assuming a one-column range
On Error Resume Next 'ignore duplicates
For lCt = 1 To UBound(vData, 1)
colUniques.Add vData(lCt, 1), CStr(vData(lCt, 1))
Next
ReDim vNewdata(1 To 1, 1 To colUniques.Count)
For lCt = 1 To colUniques.Count
vNewdata(1, lCt) = colUniques(lCt)
Next
UniqueItems = vNewdata
End Function
you might use Scripting.dictionary to get unique value fast as for exemple
Sub TestArray()
Dim arrStart() As Variant
Dim oDic As Scripting.Dictionary
arr = Array(1, 1, 1, 2, 3, 4, 4, 5)
Set oDic = uniquevalue(arr)
'Note : put data into array
Dim arrResult() As Variant
arrResult = oDic.Keys
'Note : put data into string
Dim stringResult As String
stringResult = Join(oDic.Keys, ";")
End Sub
Function uniquevalue(ByVal myArray) As Scripting.Dictionary
'Note : Add REF DLL Microsoft Srcipting Runtime before !!
'Note : Option base =0 (standard vbe param)
'Note : Array is mono dimension of any data type
Dim oDic As Scripting.Dictionary
Set oDic = New Scripting.Dictionary
For i = LBound(myArray) To UBound(myArray)
If Not oDic.Exists(myArray(i)) Then oDic.Add myArray(i), oDic.Count
Next i
Set uniquevalue = oDic
End Function

Resources