3

This is a super simple question and I haven't yet been able to piece together a solution I want from previous answers.

I have N images on a sheet and simply wish to group them. Normally I would use:

Sheets("Mail").Shapes.Range(Array(1,2,3,4,5)).Group

but obviously this doesn't work if I want to go from 1 to N. I am currently trying:

For i = 0 To Y / 33
  ReDim Preserve test(i)
  test(i) = i
Next i

Sheets("Mail").Shapes.Range(Array(test())).Group

But I am not sure how to use my test object to replicate the format I used in the non-general case. Any help appreciated!

1
  • Wouldn't your statement simply be Sheets("Mail").Shapes.Range(test).Group because test is already an array? (And you need to change test(i) = i to test(i) = i + 1) Commented Sep 8, 2017 at 10:10

2 Answers 2

5

This is a function, returning numeric arrays from M to N :

Public Function ReturnArrayAtoB(ByVal M As Long, ByVal N As Long) As Variant

    Dim lngCounter  As Long
    Dim arrReturn   As Variant

    ReDim arrReturn(N - M)

    For lngCounter = 0 To N - M
        arrReturn(lngCounter) = M + lngCounter
    Next lngCounter

    ReturnArrayAtoB = arrReturn

End Function

This is how you call it:

arrA = ReturnArrayAtoB(1, 5)
arrB = ReturnArrayAtoB(10, 12)

The first one returns Array(1,2,3,4,5) and the second one returns Array(10,11,12)

Sign up to request clarification or add additional context in comments.

4 Comments

Does this accept integer variables as arguments? I am would like to use this in the form ReturnArrayAtoB(1,Y) where Y is an integer that was defined earlier in the code. Currently it gives a Type Mismatch.
@alex1stef2 - change the long in my code to integer everywhere, it should work.
I haven't tried YowE3K's version but simply changing the longs to integers worked! Thanks for the help!
@alex1stef2 - the version with ByVal accepts both Integer and Long now
3

For completeness, there is a more concise way to create contiguous arrays using the Evaluate function:

Public Function ReturnArrayWithEvaluate(ByVal M As Long, ByVal N As Long) As Variant
    Dim vArr1 As Variant
    vArr1 = Application.Transpose(Evaluate("ROW(" & M & ":" & N & ")"))
    ReturnArrayWithEvaluate = vArr1
End Function

As Vityata observes in the comments, this method comes with some shortcomings:

  • It fails if N > ActiveSheet.Rows.Count
  • It has limited portability, as ROWS does not exist in other VBA versions, such as Access VBA
  • Performance-wise it is not worth it

I report hereby some tests, in which I vary the value of N from 10,000 to 1,000,0000 and run the two methods (for M=1).

Benchmark setup

Functions used:

Private Declare Function GetTickCount Lib "kernel32.dll" () As Long

Public Function ReturnArrayWithEvaluate(ByVal M As Long, ByVal N As Long) As Variant
    Dim vArr1 As Variant
    vArr1 = Application.Transpose(ActiveSheet.Evaluate("ROW(" & M & ":" & N & ")"))
    ReturnArrayWithEvaluate = vArr1

End Function

Public Function ReturnArrayAtoB(ByVal M As Long, ByVal N As Long) As Variant

    Dim lngCounter  As Long
    Dim arrReturn   As Variant

    ReDim arrReturn(N - M)

    For lngCounter = 0 To N - M
        arrReturn(lngCounter) = M + lngCounter
    Next lngCounter

    ReturnArrayAtoB = arrReturn

End Function

Sub test()
    Dim M As Long, N As Long
    Dim lTicks As Long
    Dim lCnt As Long, lStep As Long, lCnt2 As Long

    Dim vArrReturn As Variant
    Dim vArrResults As Variant

    M = 1
    N = 10000
    lStep = 9900 / 2
    lCnt2 = 1

    ReDim vArrResults(1 To 99 * N / lStep + 1)

    For lCnt = N To N * 100 Step lStep
        lTicks = GetTickCount
        vArrReturn = ReturnArrayAtoB(M, lCnt)
        vArrResults(lCnt2) = GetTickCount - lTicks
        lCnt2 = lCnt2 + 1
    Next lCnt
    Range("B2").Resize(lCnt2 - 1, 1).Value2 = Application.Transpose(vArrResults)
    lCnt2 = 1
    For lCnt = N To N * 100 Step lStep
        lTicks = GetTickCount
        vArrReturn = ReturnArrayWithEvaluate(M, lCnt)
        vArrResults(lCnt2) = GetTickCount - lTicks
        lCnt2 = lCnt2 + 1
    Next lCnt
    Range("C2").Resize(lCnt2 - 1, 1).Value2 = Application.Transpose(vArrResults)
End Sub

Results

The horizontal axis shows N and the vertical axis the time used by each method.

Looping vs Evaluate Ticks, for different <code>N</code>

For large arrays, evaluate is faster than looping.

The assertion that Evaluate is faster than looping is actually incorrect!

On average, the two methods consume about the same time:

-- Average Looping is 57 ticks
-- Average Evaluate is 62 ticks
-- Median ticks are 62 for both methods

Overall, I think sticking to looping is probably a better option.

5 Comments

Good shout. I've used this and this approach does provide much better performance then looping.. hence the +1
It has two drawbacks - drawback 1 - it works only in Excel (no Access etc) and drawback two - the second test with Large N/M ratio is wrong, but you did not notice - 10 million should give an error, if you are using rows in Excel. Take a look at the array that you receive - it should be Error 2015.
@Zac - read my comment and check your code again, it is dangerous to use it.
And last but not least - for the optimal usage of 2^16 times looping is way faster than using Evaluate.
@Vityata Thanks for the tips - I have revised the post with extra experiments and notes. The code should be reproducible, so if you could double check the results it would be great!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.