MAXScript Language Reference > Collections > Collection Types >

# Array Values

Value > Collections > Collection Types > Array Values

| Values and Collections - Quick Navigation

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

    <collection> as array

Convert collection to an array.

Properties

    <array>.count : Integer

Get/Set the number of elements in the array. In 3ds Max versions prior release 5, this property was read-only.

Operators

    <array>[<integer>  ]

Returns element of array. Indexes start at 1.

    <array>[<integer>] = <value>

Sets element of array to value, growing array as necessary.

    <array> + <collection>

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 $foo)
    join props getPropNames $foo #dynamicOnly
    sort props

Methods

    append <array> <value>

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:
    #(1, 2, 3, 4, 60)
    OK

    makeUniqueArray <array>

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:
    # (1, 2, 3, 4)

    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.

    deepCopy <array>

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.

    findItem <array> <value>

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.

    sort <array>

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>] [user-defined key args passed to function]

Performs binary search in a sorted list of arrays and 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 identical 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.

For 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.

NoteAs 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

    free <array>

Frees the memory used by the array value without waiting for garbage collection.

Available in 3ds Max 9 and higher.

#### See Also

- MAXScript FAQ: How do I create a multi-dimensional array?

- MAXScript FAQ: What is the differences between Arrays and BitArrays?

- MAXScript FAQ: How do I remove duplicate arrays from an array?

- BitArray Values