VBA Associative Arrays (How to Index) - arrays

I'm a newbie to VBA so please forgive my lack of experience.
Im using excel VBA and trying to figure out how to index an array. I'm importing a CSV and using the split function. I need to access each individual items of the items split into the array(s). The best way to explain what I need is an example like this from Actionscript:
var a:Array = [];
a[1] = "Hello";
a[2] = "World";
(Except that what I have is a dynamic array created by the SPLIT function)
Where I could access "Hello" with the var a[1]
Here is what I have so far:
Sub getTxtfile()
FilePath = Application.GetOpenFilename _
(Title:="Please choose a file to open", _
FileFilter:="CSV Files *.csv* (*.csv*),")
Open FilePath For Input As #1
row_number = 0
Do Until EOF(1)
Line Input #1, LineFromFile
LineItems = Split(LineFromFile, ",")
'ActiveCell.Offset(row_number, 0).Value = LineItems(1)
'ActiveCell.Offset(row_number, 1).Value = LineItems(0)
row_number = row_number + 1
'Debug.Print LineItems(0) & ": " & LineItems(1)
Loop
Close #1
End Sub
I now have 2 arrays (LineItems(0) & LineItems(1)) but how do I index what is inside of them at this point?
Thanks for any and all help, it is greatly appreciated.
Mike
The CSV I'm using is formatted to use with other applications SolidWorks, python, etc.) besides Excel. I need to access only certain elements within the array to populate certain cells. As it is...I can pull the entire array into columns but I don't want to do that, just the ones I need. Here is a sample of the CSV:
0,.200
p,1.0709
q,1.167
r,1.177
s,1.216
t,1.570
u,1.5843
v,1.6883
w,1.9079
e,.2645
What I want to do is reference the letter in the first element and have the second element inserted in a certain cell: Reference "t" through an index and have "1.570" inserted.
The elements in my arrays are LineItems(0) and LineItems(1). So ideally I'm looking to reference each indexed item in an element as LineItems(1)(a) / LineItems(1-a) or something similar to that.

I think the commented-out lines in your code should actually work, at least as far as array access is concerned. (However, I may not fully understand what you are trying to accomplish. Would you please edit your question to clarify?) I do recommend adding
Option Explicit
Option Base 0
at the top of your file, and
Dim LineItems as Variant
before the Split call. That way the compiler will help you find errors.
However, If what you really want is to open a CSV, please allow me to suggest:
Dim wb as Workbook
Workbooks.OpenText Filename:="<filename>", DataType:=xlDelimited, Comma:=True
Set wb = Workbooks(Workbooks.Count)
which will give you a new workbook wb with the CSV parsed and ready to be accessed just like any other worksheet (docs on MSDN).

You can have associative arrays in VBA with Scripting.Dictionary object or the .NET System.Collections.HashTable, but that seems a bit overkill.
You can use Jagged Arrays (Arrays of Arrays) like this:
Line = "0,.200 p,1.0709 q,1.167 r,1.177 s,1.216 t,1.570 u,1.5843 v,1.6883 w,1.9079 e,.2645"
LineItems = Split(Line, ",")
Dim LineSubItems() ' has to be Variant or Variant() array
ReDim LineSubItems(0 To UBound(LineItems))
For i = 0 To UBound(LineItems)
LineSubItems(i) = Split(LineItems(i), " ")
Next
Debug.Print LineSubItems(1)(1) ' "p"

Related

Filter out Empty elements from Array - XPATH

Context:
I'm trying to Filter out Empty values from an array. I know we can loop an array to Redim Preserve but figured there could be a way to incorporate the Filter function. However, since Filter treats all elements in an Array as String data types, there is no such thing as arr = Filter(arr, Empty, False) which I have tried in many forms.
This brought me to WorksheetFuntion.FilterXML however, I have little to none experience in XPATH and am clearly doing something wrong missing the concept of elements and nodes. I'm aware that Excel works on XPATH 1.0 and therefor I was browsing for a way to "filter" out empty elements and came accross [not(node())]. However implementing this returned an Error 1004 on retrieving arr1
Sample:
Sub Test()
Dim arr0 As Variant, arr1 As Variant, arr2 As Variant
arr0 = Array("A", "A", Empty, "A")
arr1 = WorksheetFunction.FilterXML("<t><s>" & Join(arr0, "</s><s>") & "</s></t>", "//s[not(node())]")
arr2 = WorksheetFunction.FilterXML("<t><s>" & Join(arr0, "</s><s>") & "</s></t>", "//s[string-length(text()) > 0]")
End Sub
Question:
To circumvent the issue my second option was to check the length of the strings which in this case did work and returned an array without empty elements.
Can someone enlighten me on how to improve the [not(node())] syntax to make it work.
Some explaination on why the current code doesn't work would also be highly appreciated.
Improve [Not(node())] syntax
Simply use //s[not(.='')], i.e.
arr1 = WorksheetFunction.FilterXML("<t><s>" & Join(arr0, "</s><s>") & "</s></t>", "//s[not(.='')]")
where . refers to the node content itself, compared with an empty string and negated :-)

Excel VBA: How to concatenate variant array elements (row numbers) into a range object?

I did research this question but could not find the specific answer I was looking for and am actually even more confused at present.
I created a macro that would run through rows on a sheet and run boolean checks on a number of cells in each row that looked for the presence or absence of specific values, or calculated the outcome of a specific inequality. On the basis of those checks, the macro may or may not pass the row number into a specific array. That part is working fine.
My issue is, now that I have the row numbers (stored in variant arrays) - I cannot figure out how to properly concatenate that data into a range and then take a bulk excel action on those items. What I'd like to do is create a range of those values and then delete all of those rows at once rather than looping through.
My macro is on my work computer, but here's something I wrote that should explain what I'm trying to do:
Sub Test()
Dim Str As String
Dim r As Range
Dim i, a As Integer
Dim Count As Integer
Dim RngArray()
Count = ThisWorkbook.Sheets("Sheet1").Cells(Rows.Count, "A:A").End(xlUp).Row
ReDim RngArray(Count)
a = 0
For i = 1 To Count
If Not i = Count Then
RngArray(a) = i
Str = Str & RngArray(a) & ":" & RngArray(a) & ", "
a = a + 1
ElseIf i = Count Then
RngArray(a) = i
Str = Str & RngArray(a) & ":" & RngArray(a)
a = a + 1
Else: End If
Next i
Set r = Range(Str)'Error Can Appear here depending on my concatenation technique
Range(Str).EntireRow.Delete 'error will always appear here
End Sub
I've combined a few steps here and left out any Boolean checks; in my actual macro the values in the arrays are already stored and I loop from LBound to UBound and concatenate those values into a string of the form ("1:1, 2:2, 3:3, ...., n:n")
The reason why I did this is that the rows are all over the sheet and I wanted to get to a point where I could pass the argument
Range("1:1, 2:2, 3:3, ..., n:n").EntireRow.Delete
I think it's clear that I'm just not understanding how to pass the correct information to the range object. When I try to run this I get a "Method Range of Object Global" error message.
My short term fix is to just loop through and clear the rows and then remove all of the blank rows (the macro keeps track of absolute positions of the rows, not the rows after an iterative delete) - but I'd like to figure out HOW to do this my way and why it's not working.
I'm open to other solutions as well, but I'd like to understand what I'm doing wrong here. I should also mention that I used the Join() to try to find a workaround and still received the same type of error.
Thank you.
After some experimentation with my dataset for the macro above, I discovered that it worked on small sets of data in A:A but not larger sets.
I ran Debug.Print Len(Str) while tweaking the set size and macro and found that it appears Range() can only accept a maximum of 240 characters. I still don't understand why this is or the reason for the specific error message I received, but the macro will work if Len(Str) < 240.
I'll have to loop backwards through my array to delete these rows if I want to use my present method...or I may just try something else.
Thanks to Andrew for his attention to this!

Array of 600+ Strings in excel VBA

I am doing a loop for each string in an array such that
filename = Array(file1,file2.....file600)
However VBA gets a compile error that is due to the array taking up 8 lines. As far as I am aware it only allows 1 line
(error says expected list or separator)
I am new to VBA sorry
You can escape new lines in VBA with _.
so your solution might look like
filename = Array("file1", _
"file2", _
"file3")
See How to break long string to multiple lines and If Statement With Multiple Lines
If you have 100's of names, however, you might be better off storing them in a worksheet and reading them in, rather than hard-coding them.
Should you strings in the array be actually "buildable" following a pattern (like per your examples: "file1", "file2", ...,"file600") then you could have a Function define them for you, like follows:
Function GetFileNames(nFiles As Long) As String()
Dim iFile As Long
ReDim filenames(1 To nFiles) As String
For iFile = 1 To nFiles
filenames(iFile) = "file" & iFile
Next
GetFileNames = filenames
End Function
which you'd call in your "main" code as follows
Sub main()
Dim filenames() As String
filenames = GetFileNames(600) '<--| this way'filenames' array gets filled with 600 hundred values like "file1", "file2" and so on
End Sub
The amount of code that can be loaded into a form, class, or standard module is limited to 65,534 lines. A single line of code can consist of up to 1023 bytes. Up to 256 blank spaces can precede the actual text on a single line, and no more than twenty-four line-continuation characters ( _) can be included in a single logical line.
From VB6's Help.
when programming, you don't build an array this big mannually, never.
either you store each multiline-string inside a Cell, and at the end you buid the array like this :
option Explicit
Sub ArrayBuild ()
Dim Filenames() 'as variant , and yes i presume when using multi files, the variable name should have an "s"
With Thisworkbook.sheets("temp") 'or other name of sheet
Max = .cells(.rows.count,1).end(xlup).row '=max number of rows in column 1
Filenames = .range( .cells(1,1) , .cells(Max,1)).value2 ' this example uses a one column range from a1 to a[max] , but you could also use a multi column by changing the second .cells to `.cells(Max, ColMax)`
end with
'do stuff
erase Filenames 'free memory
End Sub
An other way is to build an array like you build a house by adding one brick at a time, like this :
Dim Filenames() as string 'this time you can declare as string, but not in the previous example
Dim i& 'counter
For i=1 to Max 'same max as in previous example, adapt code plz...
redim Preserve Filenames (1 to ubound(filenames)+1) 'this is an example for unknown size array wich grows, but in your case you know the size (here Max, so you could declare it `Dim Filenames (1 to Max)` from the start, just wanted to show every option here.
Filenames(i) = Cells (i,1).value2 'for example, but can be anything else. Also i'm beeing lazy and did not reference the cell to its sheet, wich i'd never do in actual coding...
next i
EDIT i did re-read your Question, and it is even easier (basically because you ommited the bracets in your post and corrected it as comment...), use
user3598756 's code plz. I thought File1 is a variable, when it should be written as "File1" .
EDIT 2 why bother build and array where Filename(x)="Filex" anyway? you know the result beforehand

Delete duplicates within an array in vba Excel

I've got values inside an array and I would like to delete the values with are double entries, e.g. I have EUR,EUR,EUR,GBP,YEN and I like to remove the double euro entries within removing them from the execel spreadsheet.
Just save then in an array. The new array should look like that: EUR,GBP,YEN
After that I would like to write them to another spreadsheet.
My code so far:
Dim ArrayCurrency As Variant
RangeStart = "E2"
RangeEnd = "E"
RangeNew = RangeStart & ":" & RangeEnd & lRow
CurrencyArray = Range(RangeNew).Value
For Each element In CurrencyArray
Next element
I hope that you can help me!
Best regards
Matthias
Use Dictionary object for unique values - in the future.
For your current case, you may use RemoveDuplicates method on the range.

Vb.Net Sort 3x One Dimensional Arrays

I have 3 one dimensional arrays.
Each contains information that corresponds to the other 2 arrays.
e.g Array 1 contains a customer first name
Array 2 contains a customer last name
Array 3 contains the customer phone number.
This is not my actual example but is easiest to explain.
How do I sort all three arrays so that they are sorted in order by say customer last name.
If Mr Smith is sorted and has moved from position 10 to position 5 in the lastname array, I would expect his phone number and first name to also be in position 5 in the respective arrays.
I am dealing with arrays with 10,000's of items so I would like to avoid looping (my current method) as this is incredibly slow.
Hoping to use the array.sort methods.
Can someone help me?
Ok - So I have tried to use a new data Type but am still at a loss how I can instantly filter using this. Below is my sample code which has a couple of issues. If someone can resolve - it would love to learn how you did it.
The purpose of the code is to return an array containing grouped issues.
For simplicity I have assumed in the example that each constant found is an issue.
If an issue is found, combine it with other issues found on that same worksheet.
e.g The number 2 is found in both cells A1 and A2 on sheet 1. The array should return A1:A2.
If the issues are found in A1 on sheet 1 and A2 in sheet 2, two seperate array entries would be returned.
Test File and Code Here
Public Type Issues
ws_Sheet As Integer
rng_Range As String
s_Formula As String
s_Combined As String
d_ItemCount As Double
End Type
Sub IssuesFound()
Dim MyIssues() As Issues
Dim i_SheetCount As Integer
Dim s_Formula As String
Dim rng_Range As Range
Dim d_IssueCounter As Double
Dim s_SearchFor As String
Dim a_TempArray() As Issues
Dim d_InsertCounter As Double
d_IssueCounter = -1
' Loop All Sheets Using A Counter Rather Than For Each
For i_SheetCount = 1 To ActiveWorkbook.Sheets.Count
' Loop all Constants On Worksheet
For Each rng_Range In Sheets(i_SheetCount).Cells.SpecialCells(xlCellTypeConstants, 23)
If d_IssueCounter = -1 Then
' First Time and Issue Is Found, Start Recording In An Array
d_IssueCounter = d_IssueCounter + 1
ReDim MyIssues(0)
MyIssues(0).ws_Sheet = i_SheetCount
MyIssues(0).rng_Range = rng_Range.AddressLocal
MyIssues(0).s_Formula = rng_Range.Value
MyIssues(0).s_Combined = i_SheetCount & "#" & rng_Range.Value
MyIssues(0).d_ItemCount = 0
Else
' Going To Look For Issues Found On The Same Sheet with The Same Constant Value
s_SearchFor = i_SheetCount & "#" & rng_Range.Value
' HELP HERE: Need To Ideally Return Whether The Above Search Term Exists In The Array
' Without looping, and I want to return the position in the array if the item is found
a_TempArray = MyIssues 'Filter(MyIssues.s_Combined, s_SearchFor, True, vbTextCompare)
If IsVarArrayEmpty(a_TempArray) = True Then
' New Issue Found - Increase Counter By + 1
d_IssueCounter = d_IssueCounter + 1
' Increase The Array By 1
ReDim Preserve MyIssues(d_IssueCounter)
' Record The Information About The Constant Found. Sheet Number, Constant, Range, and also a combined string for searching and the array position
MyIssues(0).ws_Sheet = i_SheetCount
MyIssues(0).rng_Range = rng_Range.AddressLocal
MyIssues(0).s_Formula = rng_Range.Value
MyIssues(0).s_Combined = i_SheetCount & "#" & rng_Range.Value
MyIssues(0).d_ItemCount = 0
Else
' Get The Array Position Where Other Issues With The Same Worksheet and Constant are Stored
d_InsertCounter = a_TempArray.d_ItemCount
' Add The New Found Constant To The Range Already Containing The Same Constants on This Worksheet
MyIssues(d_InsertCounter).rng_Range = Union(rng_Range, Range(MyIssues(d_InsertCounter).rng_Range)).AddressLocal
End If
End If
Next
Next
End Sub
Function IsVarArrayEmpty(ByRef anArray As Issues)
Dim i As Integer
On Error Resume Next
i = UBound(anArray, 1)
If Err.Number = 0 Then
IsVarArrayEmpty = False
Else
IsVarArrayEmpty = True
End If
End Function
Sample Test File and Code Here
As suggested, you should not be using concurrent arrays at all. You should be defining a type with three properties and then creating a single array or collection of that type.
To answer your question though, there is no way to sort three arrays in concert but there is a way to sort two. What that means is that you can create a copy of the array that you want to use as keys and then use the copy to sort one of the other arrays and the original to sort the other. Check out the documentation for the Array.Copy overload that takes two arrays as arguments.
That said, copying the array and then sorting twice is a big overhead so you may not gain much, if anything, from this method. Better to just do it the right way in the first place, i.e. use a single array of a complex type rather than concurrent arrays of simple types. It's not 1960 any more, after all.

Resources