Array Get / Length / Last Index / Is Valid Index¶
At a glance
Lives in: Kismet Array Library; Blueprint API / Utilities / Array -
Returns: Get (a copy) returns one element value, Length and Last
Index return integers, and Is Valid Index returns a Boolean - Fails by:
an out-of-range Get logs an attempted-access warning and produces a
default-looking output; the graph continues - Official docs:
Get (a copy),
Is Valid Index,
and Array Containers
The one-minute version¶
- Array indices start at
0. IfLengthisN, valid indices are0throughN - 1. Lengthis the number of elements. An empty array has Length0.Last IndexisLength - 1. An empty array therefore has Last Index-1.Is Valid Index(Array, Index)proves onlyIndex >= 0andIndex < Lengthat that moment.Get (a copy)reads the element at one index. Value types such as numbers, Vectors, and structs are copied; changing that copy does not update the array.- For an object-reference array, the copied value is another reference to the same object, not a cloned Actor or Widget. Reassigning the reference does not replace the array slot, but changing the referenced object changes that object.
- An out-of-range Get does not branch or throw an exception. It warns and the output looks like a type default, which can hide the real indexing bug.
- A valid index can contain
Noneor a destroyed object reference. Check the index first, then check the returned object withIs Valid. - Guard empty arrays before choosing a random index. Remove while iterating in reverse, or collect removals and apply them after the read loop.
- If callbacks, events, or other logic can shrink the array between a check and a Get, validate again immediately before the access or work from a deliberate snapshot.
Length and index are different numbers¶
An index is a position. Length is a count:
InventorySlots = [Potion, Key, Torch]
Length = 3
Last Index = 2
index 0 -> Potion
index 1 -> Key
index 2 -> Torch
Arrays are zero-based because index 0 identifies the first element, index
1 the next, and so on. That makes the last position one less than the count.
| Array state | Length | Last Index | Valid indices |
|---|---|---|---|
[] |
0 |
-1 |
none |
[A] |
1 |
0 |
0 |
[A, B, C] |
3 |
2 |
0, 1, 2 |
The empty-array -1 is useful as an invalid-index sentinel. It is not an index
you can pass to Get.
Off-by-one mistake:
Get Items[Length] // always one past the end when Length > 0
Correct last-element read:
if Length > 0:
Get Items[Last Index]
or:
if Is Valid Index(Items, Last Index):
Get Items[Last Index]
What each node returns¶
| Node | Return | Empty-array result | Side effect? |
|---|---|---|---|
| Get (a copy) | Element value at Index | Out-of-range warning plus default-looking output | No |
| Length | Number of elements | 0 |
No |
| Last Index | Last valid position | -1 |
No |
| Is Valid Index | Whether 0 <= Index < Length |
false for every Index |
No |
These are pure reads. They do not add, remove, resize, sort, validate object lifetimes, or create success/failure execution pins.
Because pure nodes are evaluated when their output is needed, do not assume a Length value remains true after unrelated execution has mutated the array.
What Get (a copy) means¶
The Get node's output type follows the array element type. The word copy means the array slot itself is not returned as writable storage.
Numbers, booleans, names, vectors, and structs¶
These are value-like elements:
ItemCopy = Get InventoryEntries[2]
ItemCopy.Count = ItemCopy.Count + 1
Changing ItemCopy alone does not change InventoryEntries[2]. Write the
modified value back with Set Array Elem, or use an available by-reference
path intentionally.
This is especially easy to miss with a struct. Breaking a copied struct, changing a local field, and never assigning it back leaves the array unchanged.
Object references¶
For an array of Actor, Component, Widget, or other object references, Get copies the reference value:
EnemyRef = Get Enemies[2]
EnemyRef and Enemies[2] now point to the same Enemy object. Unreal did not
clone the Enemy.
- Calling
EnemyRef.ApplyDamage()affects that Enemy. - Setting the local
EnemyRefvariable toNonedoes not clearEnemies[2]. - Replacing the array slot requires
Set Array Elemor another array mutation. - Destroying the Enemy can leave the slot present while the reference is no longer valid.
That distinction explains why “copy” does not protect an object from mutation: the pointer-like reference was copied, not the object behind it.
When Get (a ref) is available¶
The Blueprint Get Array Item node can request a by-reference result when its source is a writable, non-const array and the graph can preserve that reference. Depending on context, right-click the Get node and choose the return-by-reference variation.
A reference result lets a compatible downstream by-reference operation address the stored element rather than a temporary copy. It is most useful for changing a struct element in place.
Use it deliberately:
- read-only or const array inputs cannot provide a writable element reference;
- not every graph connection can preserve reference semantics;
- do not treat the result as a durable handle after the array is resized, inserted into, or otherwise reallocated; and
- object-reference arrays rarely need Get-by-ref merely to call functions on the object, because Get (a copy) already points at the same object.
The clearest beginner pattern for struct updates is often still:
Is Valid Index(Entries, Index)
-> Get (a copy)
-> modify the struct copy
-> Set Array Elem(Entries, Index, ModifiedCopy)
That makes the write visible in the execution flow and remains correct when a function receives the array as read-only data.
What an out-of-range Get does¶
An index is out of range when it is negative or greater than/equal to Length:
valid = Index >= 0 AND Index < Length
Blueprint's array Get reports an attempted access outside the current array length. In editor/development output, the warning identifies the requested index and current length. Execution does not jump to a failure pin because Get has no execution pins.
The output for that failed evaluation is default-looking for the element type:
- object reference: usually
None; - integer/real number: usually
0; - Boolean: usually
false; - Name: usually
None; - Vector/Rotator/struct: default-initialized-looking fields.
Do not use that output as the failure test. A valid slot is allowed to contain
those same values. Test Is Valid Index before Get.
Bad:
Value = Get Scores[Index]
if Value == 0:
assume Index was missing
Good:
if Is Valid Index(Scores, Index):
Value = Get Scores[Index]
else:
own the missing-index path
In C++, direct TArray indexing has a stricter contract and an invalid index
can trigger a runtime bounds check/assert rather than Blueprint's recoverable
warning shape. Do not depend on invalid access in either language.
Is Valid Index does not validate the element¶
These are two separate questions:
- Does the slot exist?
Is Valid Index. - Is the object stored in that slot usable?
Is Validon the Get result.
Enemies = [BP_Enemy_1, None, BP_Enemy_3]
Is Valid Index(Enemies, 1) = true
Get Enemies[1] = None
Is Valid(Get Enemies[1]) = false
Correct object-array pattern:
Is Valid Index(Enemies, Index)
-> true: Enemy = Get Enemies[Index]
Is Valid(Enemy)
-> true: use Enemy
-> false: clear/repair the stale slot as your owner requires
-> false: handle missing position
Is Valid Index also does not prove the element has the right gameplay state.
A valid, live Enemy may already be dead, on another team, unloaded from a
logical roster, or otherwise ineligible. Those are separate policy checks.
Safe patterns for empty arrays¶
Read the first element¶
if Is Valid Index(Items, 0):
FirstItem = Get Items[0]
else:
handle empty array
Length > 0 is equally clear for the first item. Prefer Is Valid Index when
the index is dynamic or may be negative.
Read the last element¶
Last = Last Index(Items)
if Is Valid Index(Items, Last):
LastItem = Get Items[Last]
On an empty array, Last is -1, so the guard is false.
Pick a random element¶
Guard before creating the index:
if Length(Items) > 0:
RandomIndex = Random Integer in Range(0, Last Index(Items))
RandomItem = Get Items[RandomIndex]
else:
no candidate path
The range node's maximum is inclusive, which is why Last Index belongs there.
Do not feed Length into an inclusive maximum; that occasionally chooses one
past the end.
A Random Array Item helper still needs an owned empty-array policy. Its default-looking item is not proof that a candidate existed; validate the returned index or guard Length first.
Arrays that can shrink¶
A check only proves the array state at the moment of the check:
Is Valid Index(Items, SelectedIndex)
-> call event that may remove an Item
-> Get Items[SelectedIndex] // the old proof may now be stale
Events, dispatchers, interface calls, latent completion, and re-entrant functions can all run logic that changes the same owned array. Keep the check and access adjacent, and understand what every call between them can mutate.
When iteration also removes elements, forward indices shift left:
[A, B, C, D]
remove index 1 (B)
-> [A, C, D]
If a forward loop increments to index 2, it skips C because C moved to index
1.
Use one of these patterns:
Reverse index loop¶
for Index = Last Index(Items) down to 0:
if item at Index should be removed:
Remove Index(Items, Index)
Removing the current index does not change the unvisited lower indices. This assumes the loop owns the mutation and no callback removes arbitrary positions.
Collect, then remove¶
For Each Items
-> if element should be removed:
add element/index to PendingRemoval
after the read loop:
apply PendingRemoval deliberately
This is safer when callbacks or several systems participate.
Work from a snapshot¶
Copy the array intentionally when the operation needs a stable list even if the owner's live array changes during callbacks. Be explicit that the snapshot may contain object references whose objects can still become invalid.
Clamping a stale index is not always correct. It silently changes “the selected item disappeared” into “select whichever item moved into a nearby slot.” Decide whether selection should clear, follow a stable ID, or choose a replacement.
The patterns everyone actually uses¶
Selected inventory slot¶
OnSelectionChanged(NewIndex):
SelectedIndex = NewIndex
RefreshSelection:
if Is Valid Index(InventorySlots, SelectedIndex):
Entry = Get InventorySlots[SelectedIndex]
render Entry
else:
SelectedIndex = -1
render no selection
If inventory mutations can reorder slots, a stable item ID may be a better selection identity than an index.
Search result's first Actor¶
Get All Actors Of Class
-> Out Actors
-> Is Valid Index(Out Actors, 0)
true: Candidate = Get Out Actors[0]
Is Valid(Candidate)
-> use once
false: no actor found
This makes the empty result safe, but it does not make “first Actor” stable or make repeated world searches a good ownership model.
Reverse cleanup of stale references¶
for Index = Last Index(TrackedActors) down to 0:
Actor = Get TrackedActors[Index]
if NOT Is Valid(Actor):
Remove Index(TrackedActors, Index)
The index is valid because the reverse loop was formed from the current Last
Index and owns each removal. If other callbacks can mutate TrackedActors,
defer those calls or validate again.
C++ twins¶
Original example code:
if (Items.IsValidIndex(Index))
{
const FInventoryEntry ItemCopy = Items[Index];
UseItem(ItemCopy);
}
const int32 Length = Items.Num();
const int32 LastIndex = Items.Num() - 1; // -1 when empty
To mutate a stored C++ element, a non-const TArray index expression returns a
reference:
if (Items.IsValidIndex(Index))
{
FInventoryEntry& Item = Items[Index];
++Item.Count;
}
Do not keep that reference across array operations that can relocate storage.
What these nodes do not do¶
They do not:
- resize an array to make an index valid;
- turn
Lengthinto the last valid index; - validate an object stored at a valid position;
- clone an object returned from an object-reference array;
- write a changed copy back into the array;
- provide a success/failure execution branch for Get;
- keep an earlier validity result true after mutation;
- make random selection safe on an empty array; or
- make forward-loop removal preserve later indices.
Lookalikes - which one do I want?¶
| Node | Use when | Watch out |
|---|---|---|
| Get (a copy) | Read one element without exposing the slot as writable storage. | Struct/value edits do not write back; out-of-range warns. |
| Get (a ref) | A writable array and downstream operation intentionally mutate the stored element. | Availability depends on reference/const context; array mutation can invalidate the reference. |
| Length | You need the number of elements or an empty check. | Length itself is not a valid index. |
| Last Index | You need the final valid position or -1 for empty. |
Guard before Get. |
| Is Valid Index | You have a position and must prove the slot exists now. | It says nothing about an object reference inside. |
| Is Valid | You must prove an object reference is live/usable. | It does not prove which array slot it came from. |
| Set Array Elem | Replace or write back one array slot. | Index still needs a valid/intentional resize policy. |
| For Each Loop | You need every current element rather than manual indices. | Object validity and mutation policy still matter. |
Rule of thumb: Length counts, Last Index points, Is Valid Index guards the slot, Get reads the value, and Is Valid guards an object reference.
Going deeper¶
- Arrays, Sets, Maps, and ForEach - choosing a container and treating empty results as normal.
- Blueprint variables and references - value copies versus references to live objects.
- Accessed None - why a valid array slot can still produce an unusable object reference.
- Is Valid - object lifetime and pending-destruction checks, not array bounds.
- Get Actor Of Class / Get All Actors Of Class - empty search arrays and unstable “first” results.
- Small Inventory Architecture - ordered slots, stable IDs, and inventory ownership.
- Official docs: Get (a copy), Length, Last Index, Is Valid Index, and Array Containers.
- Engine source (requires engine access - we do not reproduce it here):
UKismetArrayLibrary::Array_Get,Array_Length,Array_LastIndex,Array_IsValidIndex, andGenericArray_Getare declared inEngine/Source/Runtime/Engine/Classes/Kismet/KismetArrayLibrary.hand implemented inEngine/Source/Runtime/Engine/Private/KismetArrayLibrary.cpp; the Blueprint editor's copy/reference Get-node behavior lives inUK2Node_GetArrayItemunderEngine/Source/Editor/BlueprintGraph/Private/K2Node_GetArrayItem.cpp.