24

I'm presently populating my array Securities with the following code:

Option Base 1
Securities = Array(Worksheets(3).Range("A8:A" & SymbolCount).Value)

This produces a 2-dimensional array where every address is (1...1,1...N). I want a 1-dimensional array (1...N).

How can I either (a) populate Securities as a 1-dimensional array, or, (b) efficiently strip Securities to a 1-dimensional array (I'm stuck at a with each loop).

Felix
  • 831
  • 4
  • 12
  • 16

4 Answers4

74

I know you already accepted an answer but here is simpler code for you:

If you are grabbing a singe row (with multiple columns) then use:

Securities = application.transpose(application.transpose _
             (Worksheets(3).Range("A8:A" & SymbolCount).Value))

If you are grabbing a single column (with multiple rows) then use:

Securities = application.transpose(Worksheets(3).Range("A8:A" & SymbolCount).Value)

So, basically you just transpose twice for rows and once for columns.

Update:

Large tables might not work for this solution (as noted in the comment below):

I used this solution in a large table, and I found that there is a limitation to this trick: Application.Transpose(Range("D6:D65541").Value) 'runs without error, but Application.Transpose(Range("D6:D65542").Value) 'run-time error 13 Type mismatch

Update 2:

Another problem you might have as mentioned in the comments:

If one exceeds 255 characters, the function fails.

It has been a long time since I worked with Excel VBA but this might be a general limitation of accessing the data this way?

Jon49
  • 4,444
  • 4
  • 36
  • 73
  • 3
    Agreed that this is by far the easiest way to convert a row of data into 1d array – Pynner Jan 02 '12 at 04:17
  • @Jon49 - Great trick, but this behaviour seems pretty surprising. I would have expected to just get back the same 1xn array. Do you know where this is documented? I tried google to no avail... – Emma May 28 '13 at 00:28
  • 3
    @Emma, I don't recall where I picked that trick up. But all I am doing is changing a 1xn matrix to nx1 which is just an array of n units. Not sure why the behavior is built into Excel to work that way. Many things in Excel aren't documented very well, it's such a large program! – Jon49 May 29 '13 at 22:53
  • 3
    I used this solution in a large table, and I found that there is a limitation to this trick: `Application.Transpose(Range("D6:D65541").Value)` 'runs without error, but `Application.Transpose(Range("D6:D65542").Value)` 'run-time error 13 Type mismatch – LeftyMaus Apr 29 '14 at 20:22
  • 7
    Transpose() can only handle a 1D array with up to 65,536 elements for the Excel 2007 and later versions of Excel. – Anastasiya-Romanova 秀 Aug 01 '16 at 06:27
  • 2
    I know this post is a bit old but your answer helped me. Still the best solution I've seen to achieve this! – Zac Jun 13 '19 at 14:29
  • 1
    Another limitation of `Application.Transpose` is the number of characters in each element. If one exceeds 255 characters, the function fails – Ibo Jan 13 '20 at 17:59
  • @Ibo, I added your comment to the answer. This might be a general limitation to accessing the data this way. But I don't have Excel nor do I work with Excel VBA anymore. So, I can't confirm. – Jon49 Jan 14 '20 at 18:26
  • 2
    @Jon49 I have definitely faced that problem, I tried to recreate the problem, but I could not at this time, I have written my own transpose function and I use it all the time. The built-in transpose function is not reliable for sure! 255 character limit exists in many parts of Excel, another example is the formula length for Cells' Data Validation or if using ADO to run a SQL statement if the SQL query statement exceeds 255 you cannot pass it to ADO – Ibo Jan 14 '20 at 19:10
5
Sub test2()
    Dim arTmp
    Dim securities()
    Dim counter As Long, i As Long
    arTmp = Range("a1").CurrentRegion
    counter = UBound(arTmp, 1)
    ReDim securities(1 To counter)
    For i = 1 To counter
        securities(i) = arTmp(i, 1)
    Next i
    MsgBox "done"
End Sub
iDevlop
  • 24,841
  • 11
  • 90
  • 149
  • Option (a) simply does not work. Proposed code should be quite fast anyway. – iDevlop Oct 04 '11 at 14:25
  • Sorry, newish to VBA, could you explain line 5? – Felix Oct 04 '11 at 14:37
  • As a general rule: in VBE, click on the keyword and press F1 to get help. Ubound(arTmp, 2) returns the Upper Bound of arTemp, in the 2d dimension, which is the column dimension. And now I understand your question: I may have done a mistake there... :-/ – iDevlop Oct 04 '11 at 14:41
  • I get a "subscript out of range" error for the UBound line. The line 5 reference was inquiring about the CurrentRegion line - why is that necessary? – Felix Oct 04 '11 at 15:08
  • Ah, ok. No, it's not necessary. You can use `arTmp = Range("A8:A" & SymbolCount)` as well – iDevlop Oct 04 '11 at 15:13
  • Thank you for the solution. I have never imaged that it is really quicker than the transpose. More than twice. I also realized if you use a for each cycle you gain a bit more. – Viktor West Aug 31 '20 at 10:40
3

This will reflect the answer iDevlop gave, but I wanted to give you some additional information on what it does.

Dim tmpArray As Variant
Dim Securities As Variant

'Dump the range into a 2D array
tmpArray = Sheets(3).Range("A8:A" & symbolcount).Value

'Resize the 1D array
ReDim Securities(1 To UBound(tmpArray, 1))

'Convert 2D to 1D
For i = 1 To UBound(Securities, 1)
    Securities(i) = tmpArray(i, 1)
Next

Probably the fastest way to get a 1D array from a range is to dump the range into a 2D array and convert it to a 1D array. This is done by declaring a second variant and using ReDim to re-size it to the appropriate size once you dump the range into the first variant (note you don't need to use Array(), you can do it as I have above, which is more clear).

The you just loop through the 2D array placing each element in the 1D array.

I hope this helps.

Gaijinhunter
  • 14,587
  • 4
  • 51
  • 57
  • I like the idea of bulk dumping it to 2D array. Is the looping really necessary to get 1D array? Wouldn't it be possible to `ReDim Preserve` the original 2D matrix? – Przemyslaw Remin Jan 17 '19 at 17:12
  • Are you sure (1) we need two variant arrays one for 1D and second for 2D, (2) the looping through all the variables is the fastest way? Can't we just slice or somehow ReDim the original array chopping off one dimension from it? – Przemyslaw Remin Jan 18 '19 at 08:42
2

If you read values from a single column into an array as you have it then I do think you will end up with an array that needs to be accessed using array(1, n) syntax.

Alternatively, you can loop through all cells in your data and add them into an array:

Sub ReadIntoArray()
    Dim myArray(), myData As Range, cl As Range, cnt As Integer, i As Integer
    Set myData = Worksheets(3).Range("A8:A" & SymbolCount) //Not sure how you get SymbolCount

    ReDim myArray(myData.Count)

    cnt = 0
    For Each cl In myData
        myArray(cnt) = cl
        cnt = cnt + 1
    Next cl

    For i = 0 To UBound(myArray) //Print out the values in the array as check...
        Debug.Print myArray(i)
    Next i
End Sub
Alex P
  • 12,249
  • 5
  • 51
  • 70
  • Looping through cells is slower and less efficient than dumping them into a 2D array first. – Gaijinhunter Oct 06 '11 at 04:48
  • @Issun - I agree with you. From my understading if you read in a single column range as an array then it has to be accessed using *array(1, n)* notation whereas OP wanted to be able to use *array(n)* notation. – Alex P Oct 06 '11 at 08:17
  • 2
    Yep, that's correct. The fastest way to do this is to dump the entire range into a 2D array then convert it to a 1D array (I also posted a solution on how to do this). I've tested this many times, and it's much faster than looping over cells (which I also use to do), even though looping seems to be more straight-forward. :) – Gaijinhunter Oct 06 '11 at 08:24
  • 1
    @Issun - do you have a link to your solution of converting 2D to 1D array? It would be good a learning opportunity for me... – Alex P Oct 06 '11 at 09:52
  • I posted it on this question! – Gaijinhunter Oct 06 '11 at 10:37
  • Note that the answer in this case only uses one for loop sonce it's a single column. Typically you'd need a second for loop for the second dimension. If you click my profile ive only asked 5 questions and in one I post a solution how to do both dimensions in my question! – Gaijinhunter Oct 06 '11 at 10:38