2D array from text file in excel VBA - arrays

I am opening a text file which contains text like this
asdf,zxcv,asdwqe,asdh,we5,dvsew,safhc
asdf8,asdf3,asdf4,asdf5,asdf6,asdf7,asdf2
....
I am trying to put this all into a 2d array, the only way I have been able to do anything with this so far is to read the file line by line and split that line with a comma into a 1D array and use the information that way. But I have no idea how I'm supposed to do this with a 2D array, I have googled and have found nothing that is helpful.
is there a way I can put my 1d array into a 2d array or something?
Also I do not know how many strings there are per line(but all lines will have the same amount) nor how many lines there are in the file.
EDIT: To clarify, how I want it to work is for example if I do MsgBox myArray(1,3) I want "asdf5" to be displayed in that message box.

This should work :
Sub arrayTest()
Dim arrData
Dim wbtemp As Workbook
'/ 2 = Comma (format parameter)
Set wbtemp = Workbooks.Open("C:\temp\test.txt", False, True, 2)
'/ Read in Array. range array is always 2D
arrData = wbtemp.Worksheets(1).UsedRange
wbtemp.Close (0)
'/ Range array will always start from 1. No 0 base,
'/ but given how less code one needs to write, its a fair trade off.
MsgBox arrData(2, 4)
End Sub

Related

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

VBA Associative Arrays (How to Index)

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"

Splitting text into 2d array

I am trying to split a block of text into a 2d array. I know how to split a line into an array of words, and I know how to split multiple lines into an array of lines, but I would like to do both simultaneously.
For example, I want this :
Tim is nice
Jim is mean
would become
((Tim,is,nice),(Jim,is,mean))
So Far, I have been able to use
Str1.Split(new String() {Environment.NewLine}, StringSplitOptions.None)
To make the example string into:
(Tim is nice,Jim is mean)
And I've used Str1.Split to convert "Tim is nice" into
(Tim,is,nice)
Also, if any of my code is bad syntax or anything like that let me know, I'm a Python programmer primarily.
This isnt a 2D array:
((Tim,is,nice),(Jim,is,mean))
That is a jagged array or an array of arrays. Since each line/sentence may not have the same number of words, it is a better choice. For your example, you will have an array of 2 to hold each "line", then each of those will hold an array of however many words there are.
Dim original = "Tim is nice" & Environment.NewLine & "Jim is very mean"
Dim lines = original.Split(New String() {Environment.NewLine.ToString},
StringSplitOptions.RemoveEmptyEntries)
Dim results(lines.Count - 1)() As String
For n As Int32 = 0 To lines.Count - 1
' store array of words to results(n)
results(n) = lines(n).Split(" "c)
Next
' glue a line back together and show it
Console.WriteLine(String.Join(" ", results(1)))
results(0) will hold the Tim words, results(1) will hold the Jim words. Results (of the writeline):
Jim is very mean

Filling a 3D array with 2D arrays in VBA

I have a set of data where each item has a 2D array of information corresponding to it. I'd like to create a 3D array where the first dimension is the item name and the second and third dimensions are taken up by the info corresponding to the item.
I can read the data for each item into a 2D array, but I can't figure out how to get the 2D array read into the 3D array.
I know the sizes of all the dimensions so I can create an array of fixed size before I begin the reading and writing process.
I'd like to do this by looping only through the names of the items and not looping through every cell of every 2D array.
It is easy to get the 2D arrays read in to an ArrayList but I want to be able to name the items and be able to read these back in to excel and it seems difficult to do with an ArrayList.
The question is: how do I read a 2D selection from excel into a 3D fixed sized array in VBA?
Here is an example of each approach: array of arrays or Dictionary of arrays. The Dictionary approach is considerably easier than the array of arrays if what you want is keyed lookup of values. There might be merits to the array of arrays in other cases.
This is dummy code with no real purpose but to show a couple things: grabbing a single value and an array of values. I am building a 2D array of values by grabbing some Range data which is easy and 2D. From there I build up the arrays of these values and then put them into the relevant data structure. Then I poke at the data structure to get some values out of it.
Array of Arrays approach is shown first (and outputs on the left of the picture).
Sub ArraysOfArrays()
Dim arrA() As Variant
Dim arrB() As Variant
'wire up a 2-D array
arrA = Range("B2:D4").Value
arrB = Range("F2:H4").Value
Dim arrCombo() As Variant
ReDim arrCombo(2, 1) As Variant
'name and give data
arrCombo(0, 0) = "arrA"
arrCombo(1, 0) = arrA
'add more elements
ReDim Preserve arrCombo(2, 2)
arrCombo(0, 1) = "arrB"
arrCombo(1, 1) = arrB
'output a single result
'cell(2,2) of arrA
Range("B6") = arrCombo(1, 0)(2, 2)
Dim str_search As String
str_search = "arrB"
'iterate through and output arrB to cells
Dim i As Integer
For i = LBound(arrCombo, 1) To UBound(arrCombo, 1)
If arrCombo(0, i) = str_search Then
Range("B8").Resize(3, 3).Value = arrCombo(1, i)
End If
Next i
End Sub
Couple key points here:
You can only expand the array using ReDim. ReDim is very particular that you only change the last dimension of the array when used with Preserve. Since I need one of them to track the number of entries, I do that in the second index which is... unnatural. If you know the size in advance, this painful step is skipped.
My final array is a 2xN array where the 2 contains a name and a YxZ array of data.
In order to find a given array in the mix, you have to iterate through them all.
Dictionary of Arrays is far less code and more elegant. Be sure to add the reference Tools->References in the VBA editor to Microsoft Scripting Runtime.
Sub DictionaryOfArrays()
Dim dict As New Scripting.Dictionary
'wire up a 2-D array
arrA = Range("B2:D4").Value
arrB = Range("F2:H4").Value
dict.Add "arrA", arrA
dict.Add "arrB", arrB
'get a single value
Range("F6") = dict("arrB")(2, 2)
'get a array of values
Range("F8").Resize(3, 3) = dict("arrA")
End Sub
Picture of input data and results
Data to copy if you want it (paste in B1)
a b
1 2 3 10 11 12
4 5 6 13 14 15
7 8 9 16 17 18

Resources