A Microsoft Office (Excel, Word) forum. OfficeFrustration

If this is your first visit, be sure to check out the FAQ by clicking the link above. You may have to register before you can post: click the register link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below.

Go Back   Home » OfficeFrustration forum » Microsoft Excel » Worksheet Functions
Site Map Home Register Authors List Search Today's Posts Mark Forums Read  

Array Functions



 
 
Thread Tools Display Modes
  #1  
Old February 8th, 2004, 05:37 PM
Alan Beban
external usenet poster
 
Posts: n/a
Default Array Functions

To those, if any, who use the array functions in the freely downloadable
file at http://home.pacbell.net/beban:

I refreshed the file yesterday with changes I have been making during
the past month or so. Most of the changes were tidying things up,
including generalizing many of the functions so that they would better
handle non-variant arrays. I did add one new function, Assign, which
facilitates the "direct" assignment of a range to a non-Variant() array.
I.e., in Excel the following is the normal way to load a Variant()
array from a worksheet range

Dim arr() As Variant
arr = Range("a1:j10")

but it can't be used for non-Variant() arrays. The following now can (if
the array functions are available to your workbook):

Dim arr() As Integer
Assign Range("a1:j10"), arr

Alan Beban

  #2  
Old February 9th, 2004, 03:28 AM
Harlan Grove
external usenet poster
 
Posts: n/a
Default Array Functions

"Alan Beban" wrote...
I.e., in Excel the following is the normal way to load a Variant()
array from a worksheet range

Dim arr() As Variant
arr = Range("a1:j10")

but it can't be used for non-Variant() arrays. The following now can (if
the array functions are available to your workbook):

Dim arr() As Integer
Assign Range("a1:j10"), arr


Odd bits.

If TypeName(InputArray) = "Object()" Then

You'd be assigning a range to an array of objects?! How do ranges hold
objects other than obviously as arrays of singe cells, each cell of which is
a range object in it's own right, but keeping it a multiple cell range would
make more sense than converting it into an array of individual cell range
objects.

ElseIf Not m = 1 Then

Got something against the operator?

Assign = "Success"

If the function should return success/failure, it's generally best to return
TRUE/FALSE and adopt a convention for whether TRUE represents success or
failure. Why so? Because boolean comparisons in IF statements are much more
efficient than string comparisons.


  #3  
Old February 9th, 2004, 06:08 AM
Alan Beban
external usenet poster
 
Posts: n/a
Default Array Functions

Thanks for taking the time to look at it.

Harlan Grove wrote:
Odd bits.

If TypeName(InputArray) = "Object()" Then

You'd be assigning a range to an array of objects?! How do ranges hold
objects other than obviously as arrays of singe cells, each cell of which is
a range object in it's own right, but keeping it a multiple cell range would
make more sense than converting it into an array of individual cell range
objects.


It's not clear to me that a user needs to be protected from assigning a
range to an array of objects because you or I think it doesn't make
sense to do so. As written it does permit the "direct" assignment of a
2-D Variant() array that contains objects (which myRange can be) to an
array of "Object()" type (notwithstanding that the explanation of the
function indicates that InputArray be "a dynamic array of a built-in
type other than "Object()""). In any event, it will be changed in due
course either to proscribe InputArray as Object() type, or expanded to
facilitate 1-D Variant() array assignment as well as 2-D, and the
explanation changed accordingly. At that time, a decision will be made
whether to prevent a user from assigning a range to an Object() type
array. In the meantime,

Dim myArray() As Whatever
Assign myRange, myArray

works as written if myRange is a single area multi-cell range and
Whatever is a built-in type (so long, of course, as the values in
myRange are such that an array of Whatever type will accept them).


ElseIf Not m = 1 Then

Got something against the operator?


Is there a reason for a preference? If so, it will be changed in due course.


Assign = "Success"

If the function should return success/failure, it's generally best to return
TRUE/FALSE and adopt a convention for whether TRUE represents success or
failure. Why so? Because boolean comparisons in IF statements are much more
efficient than string comparisons.


Thanks; it will be changed in due course.

Thanks again for reviewing it.

Alan Beban

  #4  
Old February 9th, 2004, 07:07 AM
Harlan Grove
external usenet poster
 
Posts: n/a
Default Array Functions

"Alan Beban" wrote...
Harlan Grove wrote:
Odd bits.

If TypeName(InputArray) = "Object()" Then

You'd be assigning a range to an array of objects?! . . .

....
It's not clear to me that a user needs to be protected from assigning a
range to an array of objects because you or I think it doesn't make
sense to do so. . . .


As so often seems to be the case, you're missing the point. It's not your or
my sensibilities that matter. This just doesn't do anything useful. The only
object type a range can contain is other range objects. All your code does
is replace single area, multiple cell ranges with arrays of ranges in which
each entry corresponds to a single cell in the original range. What
conceivable benefit would be gained from this?

. . . As written it does permit the "direct" assignment of a
2-D Variant() array that contains objects (which myRange can be) to an
array of "Object()" type . . .

....

But your code is checking *only* that InputArray is an array of objects, not
that InputRange also contains object references. When VBA tries to set an
object variable to a nonobject reference, it throws a runtime error. You've
trapped runtime errors, so nothing catastrphic happens. However, since your
function provides other types of diagnostics for unsupported argument types,
why not check that when InputArray is an array of object references, that
InputRange must also be an array of objects?


  #5  
Old February 9th, 2004, 11:23 AM
Alan Beban
external usenet poster
 
Posts: n/a
Default Array Functions

Harlan Grove wrote:
"Alan Beban" wrote...

Harlan Grove wrote:

Odd bits.

If TypeName(InputArray) = "Object()" Then

You'd be assigning a range to an array of objects?! . . .


...

It's not clear to me that a user needs to be protected from assigning a
range to an array of objects because you or I think it doesn't make
sense to do so. . . .



As so often seems to be the case, you're missing the point. It's not your or
my sensibilities that matter. This just doesn't do anything useful. The only
object type a range can contain is other range objects. All your code does
is replace single area, multiple cell ranges with arrays of ranges in which
each entry corresponds to a single cell in the original range. What
conceivable benefit would be gained from this?


No, I didn't miss the point. Setting aside for the time being your
second comment below, what it does that might conceivably be useful is
provide for the "direct" assignment of a 2-D Variant() array that
contains objects (which myRange can be) to an array of Object type.
Unless this facility were otherwise provided for (a la, e.g., your
second suggestion, below), it would be lost by the simple removal of the
If Typename(InputArray) = "Object()" section to remove the facility for
the useless assignment of a range; though that facility might not make
sense, I don't see the harm in leaving it in--i.e., letting that section
apply to both ranges (though useless) and some arrays (conceivably useful).

[Y]our code is checking *only* that InputArray is an array of objects, not
that InputRange also contains object references. When VBA tries to set an
object variable to a nonobject reference, it throws a runtime error. You've
trapped runtime errors, so nothing catastrophic happens. However, since your
function provides other types of diagnostics for unsupported argument types,
why not check that when InputArray is an array of object references, that
InputRange must also be an array of objects?


The code does not provide *any* checking to see that InputRange contains
only values that are acceptable to an array of the type that InputArray
is. It does not, for example, check that InputRange does not contain any
"pure strings" (such as "OK") when InputArray is of type Integer or Long
or Single or Double. This did not, and does not, seem straightforward to
me and I left that to the errorhandler. I did not, for example, try to
work out code that would exclude InputRange dates when InputArray was of
Long type, or exclude InputRange integers when InputArray was of Date
type, or, as you suggest above, exclude InputRange non-objects when
InputArray is of Object type. I'm not sure what the code would look like
in this last case, so I can't be sure that working it out for the sole
purpose of removing a perhaps useless, but harmless, functionality of
permitting the assigning of ranges to Object type arrays would be worth it.

Thanks again for taking time. I do appreciate the substantive comments.

Alan Beban




  #6  
Old February 9th, 2004, 03:14 PM
Alan Beban
external usenet poster
 
Posts: n/a
Default Array Functions

The relevant code will go like this:

If TypeName(InputArray) = "Object()" Then
If Not TypeOf InputRange Is Range Then
'Load 1-D or 2-D Object() InputArray
Else
'message "Can't assign range to Object() array"
Exit Function
End if
Else
'Load 1-D or 2-D non-Object() array
End If
Assign = True
errorhandler:
End Function

Thanks for helping me think it through.

Alan Beban

Harlan Grove wrote:
"Alan Beban" wrote...

Harlan Grove wrote:

Odd bits.

If TypeName(InputArray) = "Object()" Then

You'd be assigning a range to an array of objects?! . . .


...

It's not clear to me that a user needs to be protected from assigning a
range to an array of objects because you or I think it doesn't make
sense to do so. . . .



As so often seems to be the case, you're missing the point. It's not your or
my sensibilities that matter. This just doesn't do anything useful. The only
object type a range can contain is other range objects. All your code does
is replace single area, multiple cell ranges with arrays of ranges in which
each entry corresponds to a single cell in the original range. What
conceivable benefit would be gained from this?


. . . As written it does permit the "direct" assignment of a
2-D Variant() array that contains objects (which myRange can be) to an
array of "Object()" type . . .


...

But your code is checking *only* that InputArray is an array of objects, not
that InputRange also contains object references. When VBA tries to set an
object variable to a nonobject reference, it throws a runtime error. You've
trapped runtime errors, so nothing catastrphic happens. However, since your
function provides other types of diagnostics for unsupported argument types,
why not check that when InputArray is an array of object references, that
InputRange must also be an array of objects?



  #7  
Old February 9th, 2004, 07:08 PM
Harlan Grove
external usenet poster
 
Posts: n/a
Default Array Functions

"Alan Beban" wrote...
Harlan Grove wrote:

...
The code does not provide *any* checking to see that InputRange contains
only values that are acceptable to an array of the type that InputArray
is. It does not, for example, check that InputRange does not contain any
"pure strings" (such as "OK") when InputArray is of type Integer or Long
or Single or Double. This did not, and does not, seem straightforward to
me and I left that to the errorhandler. I did not, for example, try to
work out code that would exclude InputRange dates when InputArray was of
Long type, or exclude InputRange integers when InputArray was of Date
type, or, as you suggest above, exclude InputRange non-objects when
InputArray is of Object type. I'm not sure what the code would look like
in this last case, so I can't be sure that working it out for the sole
purpose of removing a perhaps useless, but harmless, functionality of
permitting the assigning of ranges to Object type arrays would be worth it.


OK, you're the one supposedly championing generality. An array of Variants could
contain scalars and objects. For example,


Sub xyz()
Dim v() As Variant
ReDim v(1 To 2)
v(1) = 5#
Set v(2) = ActiveCell
End Sub


Your stated concern for generality would be easier to believe if your code
looked like


For i = LBound(InputArray, 1) To UBound(InputArray, 1)

For j = LBound(InputArray, 2) To UBound(InputArray, 2)

If IsObject(InputRange(i, j)) Then
Set InputArray(i, j) = InputRange(i, j)

Else
InputArray(i, j) = InputRange(i, j)

End If

Next j

Next i


You didn't consider this possibility, did you?

So, object support only seems worthwhile when InputRange is an array of
Variants. In that case, wouldn't the BUILT-IN VBA syntax/semantics for assigning
an array of variants to a nonarray variant provide *exactly* the same
functionality as using Assign to assign an array of variants to another array of
variants? The former would be MUCH QUICKER because VBA itself would be doing
everything (and it would handle hierarchical arrays, any dimensions, and all the
other annoying possibilities Assign doesn't). So what's the benefit from doing
this? You keep repeating that providing this does no harm. Yes it does! It
wastes system resources adding limited, slow functionality which provides no
benefits. Dead and do-nothing code should be eliminated whenever possible.
You're doing no one any favors adding 'features' no sane person would ever use.

--
To top-post is human, to bottom-post and snip is sublime.
  #8  
Old February 9th, 2004, 11:49 PM
Harlan Grove
external usenet poster
 
Posts: n/a
Default Array Functions

"Alan Beban" wrote...
Harlan Grove wrote:

...
. . . As I mentioned previously, by design no checking is
provided to insure that each element of InputRange is of a type that
will be accepted by an array of the type of InputArray; that is left to
errorhandler. One reason, among others, is that otherwise, as above,
an additional call is required within the loop for each element of
InputRange.

...

You're checking whether InputArray is an array of object references, but you see
no benefits to checking whether individual entries in InputRange are objects?
And you base this on the desirability of not checking the types of any entry in
InputRange.

As for the need for additional calls, if more calls provide more functionality,
it's a design trade-off. So you seem to be taking the position that the
functionality provided by Assign as you have it so far is ideal even though it
won't handle assigning an any valid InputRange (no more than 2 non-hierarchical
dimensions of variants) to an array of variants.

The only time Assign could do anything useful when InputArray is an array of
object references is when InputRange is also an array of object references.
Otherwise, the error handler would kick in. Even when both are arrays of object
references, when would Assign ever be preferable to


Dim InputArray As Variant, InputRange(...) As SomeObjectType
'initialize InputRange
InputArray = InputRange


?!!

You must have some idea when Assign would be useful when InputArray is an array
of object references. Don't you?


I don't understand your discussion above about built-in VBA
syntax/semantics (I'm not sure what syntax/semantics you're referring
to) being quicker than using Assign to assign an array of variants to
another array of variants. . . .


See above. Precisely *NOT* assignment to another *ARRAY* of variants but to a
variant, as in 'Dim x As Variant', not 'Dim x() As Variant'.

. . . The function isn't proposed for use in
assigning an array of variants to another array of variants; it's
proposed for assigning ranges and arrays to non-Variant() arrays. I.e.,
cases, admittedly not all, in which the "Can't assign to array" error
message is normally encountered. Indeed, to assign your Variant() array
v above to a Variant() array arr, all that's needed is arr = v; Assign
isn't relevant to that case. Unless once again, I'm missing some point.
If so, perhaps you could make the point more clearly, without so much
clutter.


OK, it seems your original conception was that Assign would handle assigning
ranges to arrays of nonvariant and nonobject type. For example, assigning a
range to an array of integers (along with all the implicit conversion and
rounding that may entail). Then you seem to have thought about adding support
for 1D and 2D arrays as well as ranges. Then you seem to have thought that such
arrays could contain object references. Classic feature creep.

Assigning anything to an array of variants is pointless compared to assinging
exactly the same thing to a single, non-array variant (see example above if
you're still unclear on the concept). In that sense, Assign provides no benefits
when InputArray is an array of variants. It also provides no clear benefits when
assigning arrays of objects to arrays of objects. Maybe I've been too sheltered,
but when I need arbitrary collections of objects, I use Collection objects
rather than arrays of objects. When are arrays of objects useful? How often do
you use them?

Anyway, FTHOI, here's how I'd do it. Note it doesn't allow objects at all, it
requires that the target array start off empty (up to the caller to Erase it if
necessary) and it does lightweight error checking by entry. Oh, and it also
handles 0D through 6D nonhierarchical arrays.


Function ct(ByRef a As Variant, ByRef b As Variant) As Boolean
Dim n As Long, x As Variant
Dim i1 As Long, i2 As Long, i3 As Long, i4 As Long, i5 As Long, i6 As Long

ct = True 'error exit status is TRUE, success is FALSE

If IsObject(a) Or IsObject(b) Then Exit Function

If Not (IsArray(a) And IsArray(b)) Then 'both scalars
Select Case TypeName(b)
Case "Boolean": b = CBool(a)
Case "Byte": b = CByte(a)
Case "Currency": b = CCur(a)
Case "Date": b = CDate(a)
Case "Decimal": b = CDec(a)
Case "Double": b = CDbl(a)
Case "Integer": b = CInt(a)
Case "Long": b = CLng(a)
Case "Single": b = CSng(a)
Case "String": b = CStr(a)
Case "Variant": b = CVar(a)
Case Else: Exit Function 'impossible condition - error
End Select

ct = False 'success if any built-in scalar type

ElseIf IsArray(a) And IsArray(b) Then
On Error Resume Next

x = UBound(b, 1)
If Err.Number = 0 Then
Exit Function 'b must be empty!!
Else
Err.Clear
End If

n = 1
Do 'forever
x = UBound(a, n + 1)
If Err.Number 0 Then Exit Do
n = n + 1
Loop
Err.Clear

On Error GoTo 0


Select Case n

Case 1:
ReDim b( _
LBound(a, 1) To UBound(a, 1) _
)

For Each x In b
Exit For
Next x

For i1 = LBound(a, 1) To UBound(a, 1)
If Not ct(a(i1), x) Then
b(i1) = x
Else
Exit Function 'error converting a(...)
End If
Next i1

Case 2:
ReDim b( _
LBound(a, 1) To UBound(a, 1), _
LBound(a, 2) To UBound(a, 2) _
)

For Each x In b
Exit For
Next x

For i1 = LBound(a, 1) To UBound(a, 1)
For i2 = LBound(a, 2) To UBound(a, 2)
If Not ct(a(i1, i2), x) Then
b(i1, i2) = x
Else
Exit Function 'error converting a(...)
End If
Next i2
Next i1

Case 3:
ReDim b( _
LBound(a, 1) To UBound(a, 1), _
LBound(a, 2) To UBound(a, 2), _
LBound(a, 3) To UBound(a, 3) _
)

For Each x In b
Exit For
Next x

For i1 = LBound(a, 1) To UBound(a, 1)
For i2 = LBound(a, 2) To UBound(a, 2)
For i3 = LBound(a, 3) To UBound(a, 3)
If Not ct(a(i1, i2, i3), x) Then
b(i1, i2, i3) = x
Else
Exit Function 'error converting a(...)
End If
Next i3
Next i2
Next i1

Case 4:
ReDim b( _
LBound(a, 1) To UBound(a, 1), _
LBound(a, 2) To UBound(a, 2), _
LBound(a, 3) To UBound(a, 3), _
LBound(a, 4) To UBound(a, 4) _
)

For Each x In b
Exit For
Next x

For i1 = LBound(a, 1) To UBound(a, 1)
For i2 = LBound(a, 2) To UBound(a, 2)
For i3 = LBound(a, 3) To UBound(a, 3)
For i4 = LBound(a, 4) To UBound(a, 4)
If Not ct(a(i1, i2, i3, i4), x) Then
b(i1, i2, i3, i4) = x
Else
Exit Function 'error converting a(...)
End If
Next i4
Next i3
Next i2
Next i1

Case 5:
ReDim b( _
LBound(a, 1) To UBound(a, 1), _
LBound(a, 2) To UBound(a, 2), _
LBound(a, 3) To UBound(a, 3), _
LBound(a, 4) To UBound(a, 4), _
LBound(a, 5) To UBound(a, 5) _
)

For Each x In b
Exit For
Next x

For i1 = LBound(a, 1) To UBound(a, 1)
For i2 = LBound(a, 2) To UBound(a, 2)
For i3 = LBound(a, 3) To UBound(a, 3)
For i4 = LBound(a, 4) To UBound(a, 4)
For i5 = LBound(a, 5) To UBound(a, 5)
If Not ct(a(i1, i2, i3, i4, i5), x) Then
b(i1, i2, i3, i4, i5) = x
Else
Exit Function 'error converting a(...)
End If
Next i5
Next i4
Next i3
Next i2
Next i1

Case 6:
ReDim b( _
LBound(a, 1) To UBound(a, 1), _
LBound(a, 2) To UBound(a, 2), _
LBound(a, 3) To UBound(a, 3), _
LBound(a, 4) To UBound(a, 4), _
LBound(a, 5) To UBound(a, 5), _
LBound(a, 6) To UBound(a, 6) _
)

For Each x In b
Exit For
Next x

For i1 = LBound(a, 1) To UBound(a, 1)
For i2 = LBound(a, 2) To UBound(a, 2)
For i3 = LBound(a, 3) To UBound(a, 3)
For i4 = LBound(a, 4) To UBound(a, 4)
For i5 = LBound(a, 5) To UBound(a, 5)
For i6 = LBound(a, 6) To UBound(a, 6)
If Not ct(a(i1, i2, i3, i4, i5, i6), x) Then
b(i1, i2, i3, i4, i5, i6) = x
Else
Exit Function 'error converting a(...)
End If
Next i6
Next i5
Next i4
Next i3
Next i2
Next i1

Case Else: Exit Function 'impossible condition - error

End Select

ct = False 'success if any built-in scalar type

'Else -- mixed references - unsupported - error

End If

End Function


Sample usage:


Sub foo()
Dim x() As Byte

If ct(Range("A1:C5").Value, b) Then
MsgBox "FUBAR!"
Exit Sub
End If

'other stuff using b

End Sub

--
To top-post is human, to bottom-post and snip is sublime.
  #9  
Old February 10th, 2004, 12:49 AM
Harlan Grove
external usenet poster
 
Posts: n/a
Default Array Functions

"Harlan Grove" wrote...
...
Sub foo()
Dim x() As Byte


I love typos! Make that

Dim b() As Byte


  #10  
Old February 10th, 2004, 01:34 AM
Alan Beban
external usenet poster
 
Posts: n/a
Default Array Functions

Harlan Grove wrote:
Anyway, FTHOI, here's how I'd do it. Note it doesn't allow objects at all, it
requires that the target array start off empty (up to the caller to Erase it if
necessary) and it does lightweight error checking by entry. Oh, and it also
handles 0D through 6D nonhierarchical arrays.


With 1 through 9 in a1:c3, what's the call to assign the range to an
Integer() type array?

When I code

Sub foo()
Dim arr() As Integer
x = ct(Range("A1:C3"), arr)
Debug.Print x
End Sub

it fails because Range("A1:C3") is an object.

Alan Beban

 




Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Forum Jump


All times are GMT +1. The time now is 01:48 PM.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Copyright ©2004-2024 OfficeFrustration.
The comments are property of their posters.