Array Values
An array is a variable length indexable sequence of values.
The values in an array can be of any type.
Array values are mappable.
Literals
#(<value>, <value>, ...)
#() -- an empty array
See also Array Literals.
Constructors
Convert collection to an array.
Properties
Get/Set the number of elements in the array. In 3ds Max versions prior release 5, this property was read-only.
Operators
Returns element of array. Indexes start at 1.
<array>[<integer>] = <value>
Sets element of array to value, growing array as necessary.
Like join except a completely new array is constructed containing all the elements of the first
and second operands
EXAMPLE:
|
a = #(1,2,3,4)
join a #(5,6,7,8)
(cameras as array) + lights
props = getPropNames Node
join props (getPropNames(classOf Box))
join props (getPropNames Box #dynamicOnly)
sort props
|
Methods
Append value to array, growing it as necessary. Returns the resulting array.
appendIfUnique <array> <value>
Appends the value to the array if the value is not already in the array.
Returns true if the value is added, false if not.
Available in 3ds Max 2008 and higher. Previously available in the free Avguard Extensions.
EXAMPLE:
|
(
local h = #(1,2,3,4)
appendIfUnique h 60 -- should work
appendIfUnique h 4 -- shouldn't work
if ((h.count != 5) or (h[5] != 60)) do
throw "Entry 5 in the array is incorrect"
format "%\n" h
)
|
RESULT:
|
|
Returns a new array with only unique members of source array.
Available in 3ds Max 2008and higher. Previously available in the free Avguard Extensions.
EXAMPLE:
|
makeUniqueArray # (1, 2, 3, 4, 3, 2, 1)
|
RESULT:
|
|
copy <array> [#noMap] -- mapped
Creates a copy of all the elements in the array. Returns a value of OK .
If #noMap is specified, the copy made is what is called a shallow copy - only a copy of the upper-level value itself (that is, the array value) is created. Copies aren't made of values stored in the array, rather a reference
to the same value is stored in both array values.
Creates a copy of all elements in the array, including nested sub-arrays and returns
the new array as the result.
Available in 3ds Max 9 and higher.
COPY AND DEEPCOPY EXAMPLES:
|
--REFERENCING WITHOUT COPYING:
a = #(10,# (1,2,3),30,"test") --create an array
--> # (10, # (1, 2, 3), 30, "test")
b = a --array is b is referencing the same array a
--> # (10, # (1, 2, 3), 30, "test")
b[1] = 5 --so if we change an element in b, a will also change!
--> 5
a
--> # (5, # (1, 2, 3), 30, "test")
b
--> # (5, #(1, 2, 3), 30, "test")
|
--#NOMAP SHALLOW COPY - TOP-LEVEL VALUE COPIED:
a = # (10,#(1,2,3),30,"test") --create an array
--> #(10, #(1, 2, 3), 30, "test")
b = copy a #nomap --create a shallow copy
--> #(10, #(1, 2, 3), 30, "test")
b[1] = 5 --changing a top-level element in b does not change a:
--> 5
b
--> #(5, #(1, 2, 3), 30, "test")
a
--> #(10, #(1, 2, 3), 30, "test")
--But if we change the sub-array in b, a will also change.
--This is because the sub-arrays are not copied recursively.
--See deepcopy() below for a solution.
b[2][2] = 42
--> 42
b
--> #(5, #(1, 42, 3), 30, "test")
a
--> #(10, #(1, 42, 3), 30, "test")
--Same applies to the string which is similar to an array
b[4][2] = "E" -- changing the second character in b affects a:
--> "E"
b
--> #(5, #(1, 42, 3), 30, "tEst")
a
--> #(10, #(1, 42, 3), 30, "tEst")
|
-- #DEEPCOPY COPY - ALL LEVELS COPIED:
a = # (10,# (1,2,3),30,"test") -- create an array
--> # (10, # (1, 2, 3), 30, "test")
b = deepcopy a -- create a deep copy
--> # (10, # (1, 2, 3), 30, "test")
b[1] = 5 -- changing a value in array b has no effect on array a
--> 5
b
--> # (5, # (1, 2, 3), 30, "test")
a
--> # (10, # (1, 2, 3), 30, "test")
b[2][2] = 42 -- changing a sub-array value in b has no effect on a
--> 42
b
--> # (5, # (1, 42, 3), 30, "test")
a
--> # (10, # (1, 2, 3), 30, "test")
-- Same applies to the string which is similar to an array
b[4][2] = "E" -- changing the string in b does NOT affect a:
--> "E"
b
--> # (5, # (1, 42, 3), 30, "tEst")
a
--> # (10, # (1, 2, 3), 30, "test")
|
Using an equality comparison expression on two arrays, even if the one is a deep copy of the other, will not perform per-element
comparison but will check whether the two variables reference the same data in memory.
Thus, the result of such a comparison will always be false unless the two variables
point at the same memory address:
EXAMPLE:
|
a = # (1,2,3,4,5,10,42) --create an array and store in variable a
--> # (1, 2, 3, 4, 5, 10, 42)
b = deepcopy a --deep-copy the array into variable b
--> # (1, 2, 3, 4, 5, 10, 42)
a == b --although the two have the same elements, they are not considered equal
--> false
c = a --if variable c is referencing the same array in memory,
--> # (1, 2, 3, 4, 5, 10, 42)
c == a --the two are considered equal
--> true
--To compare arrays a and b, we have to compare their elements.
--If no elements are compound values (like other arrays, matrix3 values etc.),
--the following expression would work:
a.count == b.count AND \
(for i = 1 to a.count where a[i]!=b[i] collect i).count == 0
--> true
--A simpler alternative is to compare the two arrays by turning them into strings.
--We have to use the 'with PrintAllElements on' context because otherwise
--longer arrays would be shortened after the 20th element:
with PrintAllElements on a as string == b as string
--> true
|
deleteItem <array> <number>
Delete element indexed by number from array, shrinking it in size by 1. Returns the
resulting array.
join <array> <collection>
Appends all the elements of the second argument to the array (first) argument.
insertItem <value> <array> <integer>
Inserts the value at the specified index in the array, growing the array if necessary.
Returns OK.
Does a MAXScript '==' comparison between elements in the array and the target object and then returns the
index of the first occurrence of the given value in the array or 0 (zero) if the value is not in the array.
Values such as Point3s and Strings match if they have the same contents.
Sorts the elements of the array into ascending order. All the elements must be comparable.
qsort <array> <function> [start:<integer>] [end:<integer>] [user-defined key args passed to function]
Sorts the array using the specified function for element-by-element comparison.
The comparison function must take 2 values as arguments, and return an integer number
< 0 if the first value is less than the second value, 0 if the values are equivalent,
and an integer number > 0 if the first value is greater than the second value.
The entire array is sorted unless a start or end value is specified.
EXAMPLE:
|
The following script generates 10 random positions, and then sorts the positions based
on their distance from [0,0,0]:
|
positions =for i = 1 to 10 collect (random [0,0,0] [100,100,0])
fn compareFN v1 v2 =
(
local d = (length v1)-(length v2)
case of
(
(d < 0.): -1
(d > 0.): 1
default: 0
)
)
qsort positions compareFN
for p in positions do print p
|
All key arguments are passed to the comparison function. This allows you to do things
like an indexed sort:
EXAMPLE:
|
fn compareFN v1 v2 valArray: =
(
local v1i = valArray[v1]
local v2i = valArray[v2]
local d = (length v1i) - (length v2i)
case of
(
(d < 0.): -1
(d > 0.): 1
default: 0
)
)
positions = for i = 1 to 10 collect (random [0,0,0] [100,100,0] )
indexArray = for i = 1 to positions.count collect i
qsort indexArray compareFN valArray:positions
for i = 1 to positions.count do print positions [indexArray[i] ]
|
bsearch <key> <array> <function> [start:<integer>] [end:<integer>] [index:&variable] [user-defined key args passed to function]
Performs binary search in a sorted list of arrays, returning the found element, or
undefined if no match is found. Can be used to efficiently find an item in an array
by a search key in 3ds Max 2009 and higher.
The <key> argument defines the search key. It will be passed to the search function
as its first argument.
The <array> argument is the array to search for the key in.
The <function> argument is a user-defined search function. By default, it will be
passed two arguments - the search key and the current element of the array. The function
is similar to the one used by the qsort() method described earlier on this page.
Additional optional keyword arguments can be defined in the function and passed to
it by name from the bsearch call.
The start: and end: optional keyword arguments can be used to limit the search in the given range.
EXAMPLE:
|
-- Compare function used by bsearch:
fn LookupTableComparator a b = (
if a[1] > b[1] then 1
else if a[1] < b[1] then -1
else 0
)
searchTable =for i = 1 to 10 collect #( (i as string), "Test"+(i as string), i)
bsearch "4" searchTable LookupTableComparator
-- output: #("4", "Test4", 4)
bsearch "0" searchTable LookupTableComparator
-- output: undefined
|
Since 3ds Max 2015, if the optional by-reference keyword argument index: is specified, the index of the found item will be written into it.
In version prior to 3ds Max 2015, there is an indirect way to achieve the same result:
EXAMPLE:
|
fn compareFn val index valArray: =
(
arrayVal = valArray[index]
format "val = % index = % arrayVal = %\n" val index arrayVal
-- do comparison of val and arrayVal here:
case of (
(val < arrayVal): -1
(val > arrayVal): 1
default: 0
)
)
--valArray is the sorted array:
valArray = #(1, 16, 20, 22, 30, 100, 234)
indexArray = for i = 1 to valArray.count collect i
foundIndex = bsearch 20 indexArray compareFn valArray:valArray
format "Index was %\n" foundIndex
--Since bsearch returns the found element, and we are using the indices as elements,
--the variable foundIndex will contain the index of the found element instead of the valArray element itself.
|
OUTPUT:
|
compareFn()
#(1, 16, 20, 22, 30, 100, 234)
#(1, 2, 3, 4, 5, 6, 7)
val = 20 index = 4 arrayVal = 22
val = 20 index = 2 arrayVal = 16
val = 20 index = 3 arrayVal = 20
3
Index was 3
OK
|
For additional examples and detailed benchmarks of bsearch() compared to MAXScript-based search functions, see Using Bsearch For Fast Table Lookup in the "How To Make It Faster" chapter of the Frequently Asked Questions section.
amin ( <array> | {value} )
Returns minimum value of items in the array or of the argument values. If the array
size is zero or no arguments are specified, the value undefined is returned.
EXAMPLE:
|
myMin1 = amin #(5,1,4,2,8)
myMin2 = amin 5 1 4 2 8
|
amax ( <array> | {value} )
Returns maximum value of items in the array or of the argument values. If the array
size is zero or no arguments are specified, the value undefined is returned.
NOTE:As described in
Reference Assignment, the elements in an array are stored by reference rather than by value. When you
operate directly on array elements, and the reference to the array is stored in more
than one place, unexpected results may occur. An example of this is shown in the following
script.
SCRIPT:
|
RandomSets=#() -- create array
RandomSet=#() -- create array
for i = 1 to 3 do -- loop i
(for j=1 to 3 do -- for each i loop j
RandomSet[j] = random 1 100 -- set array element to random value
print RandomSet #nomap -- print array of random values
RandomSets[i] = RandomSet -- store random value array in array
)
print RandomSets -- print array of arrays of random values
|
OUTPUT:
|
#() -- result of line 1
#() -- result of line 2
#(33, 47, 31) -- output from line 6, i =1
#(4, 52, 39) -- output from line 6, i =2
#(52, 36, 23) -- output from line 6, i =3
OK -- result of for loop, lines 3 to 8
#(52, 36, 23) -- 3 lines of output from line 7
#(52, 36, 23)
#(52, 36, 23)
OK -- result of line 9
|
As can be seen in the output lines 7 to 9, all the elements of array RandomSets contain the same values, which is not the result as expected from the output shown
on lines 3 to 5. The reason the same values are stored in each element of RandomSets is only one array value is actually ever created for RandomSet (in line 2), and line 5 of the script is only changing the elements of that array
value. Thus each element of RandomSets is actually pointing at the exact same value.
The easiest fix for the above script is to move line 2 into the for i loop expression, thereby creating a new array value which will then be stored in
RandomSets.
|
SCRIPT:
|
RandomSets =# ()
for i = 1 to 3 do
( RandomSet =# () --create a new array value for RandomSet
for j = 1 to 3 do
RandomSet[j]=random 1 100
print RandomSet #nomap
RandomSets[i]=RandomSet
)
print RandomSets
|
OUTPUT:
|
#()
#(89, 27, 88)
#(87, 87, 10)
#(74, 27, 64)
OK
#(89, 27, 88)
#(87, 87, 10)
#(74, 27, 64)
OK
|
You can convert object sets and wild-card pathnames to arrays using the as operator.
This has the effect of taking a "snapshot" into the array of the current objects in
the set or those matched by the pathname, so that you can then later work on that
collection of objects without worrying if the set or match has changed.
This is similar to named selection sets in the 3ds Max user interface, and can be used, for example, to keep track of selections as you
work interactively with MAXScript.
If the user deletes from the scene any of the objects in the array, you will get an
error if you try to map operations over the array.
EXAMPLES:
|
sel1 = selection as array
boxes_at_load = $box* as array
snap_children = $torso...* as array
original_cameras = cameras as array
|
Frees the memory used by the array value without waiting for garbage collection.
Available in 3ds Max 9 and higher.