Array Add / Add Unique / Insert / Remove / Find / Contains¶
At a glance
Lives in: Kismet Array Library; Blueprint API / Utilities / Array -
Mutates the array: Add, Add Unique, Insert, Remove Index, and
Remove Item - Searches without changing it: Find Item and
Contains Item - Important returns: Add gives the new index; Add Unique
gives the new index or -1 when an equal item was already present; Find
gives the first equal item's index or -1; Remove Item gives a Boolean -
Official docs: Kismet Array Library,
Add Unique,
Remove Item,
and Find Item
The one-minute version¶
- Add always appends one item, allows duplicates, and returns its new zero-based index.
- Add Unique searches for an equal item first. If none exists, it appends
and returns the new index. If one already exists, it changes nothing and the
Blueprint node returns
-1(INDEX_NONE) - it does not return the existing item's index. - Insert puts an item before the current item at an index and shifts that
item plus everything after it one position right.
Index == Lengthis a valid append; the valid insertion range is0..Length. - Remove Index removes exactly one position. Remove Item removes every
equal occurrence and returns
trueif at least one was removed. - Find Item returns the first equal item's index, or
-1when missing. Contains Item answers the same membership question as a Boolean but does not tell you where the item is. - Array equality means the element type's equality: values compare as values, structs normally compare their fields, and object references compare which object they point to - not whether two Actors happen to look alike.
Find,Contains, andAdd Uniquedo not prove an object reference is usable. An array can containNoneor a reference to an Actor being destroyed; use Is Valid separately.- Inserting or removing changes indices. Treat an array For Each as read-only; remove by reverse index, build a filtered replacement, or collect removals and apply them after the loop.
What each node actually does¶
Start with this array:
Items = [Potion, Key, Potion]
index 0 1 2
| Node | Result | Array afterward |
|---|---|---|
Add(Torch) |
returns 3 |
[Potion, Key, Potion, Torch] |
Add Unique(Key) |
returns -1 |
unchanged; an equal Key already exists |
Add Unique(Map) |
returns 3 |
[Potion, Key, Potion, Map] |
Insert(Map, 1) |
no data return | [Potion, Map, Key, Potion] |
Remove Index(1) |
no data return | [Potion, Potion] |
Remove Item(Potion) |
returns true |
[Key]; both Potions are removed |
Find Item(Potion) |
returns 0 |
unchanged; only the first match is reported |
Contains Item(Torch) |
returns false |
unchanged |
The mutation nodes have execution pins because they change the connected array variable. Find and Contains are pure reads: they scan the current array when a downstream value needs their output.
pseudocode of the Blueprint behavior - not engine source
function Add(Array, NewItem):
append NewItem
return Length(Array) - 1
function AddUnique(Array, NewItem):
if Find(Array, NewItem) != -1:
return -1
return Add(Array, NewItem)
function Find(Array, Item):
for Index from 0 to Length(Array) - 1:
if Array[Index] is equal to Item:
return Index
return -1
function RemoveItem(Array, Item):
remove every element equal to Item, preserving remaining order
return whether anything was removed
These are array operations, so Find, Contains, Add Unique, and Remove Item may walk many elements. That is perfectly reasonable for a small inventory or overlap list during an event. Repeating a large linear search several times per frame is a signal to cache the result, use a Set for membership, or use a Map for keyed lookup.
Add, Add Unique, and Insert are different promises¶
Add: duplicates are data¶
Use Add when repeated values are meaningful:
DamageNumbers = [12, 12, 25]
PatrolRoute = [PointA, PointB, PointA]
InventorySlots = [Potion, Potion, Key]
Its return is the index assigned to this newly appended element. If the old
Length was 3, Add returns 3; the new Length becomes 4.
Add Unique: equality is the gate¶
Add Unique is useful when the array must not gain another equal value:
ProcessedActors = [EnemyA, EnemyB]
Add Unique(EnemyA) -> -1 and no change
Add Unique(EnemyC) -> 2 and EnemyC is appended
The -1 result means not newly added. If you need the existing index, call
Find Item. Do not feed Add Unique's -1 directly into Get.
Add Unique prevents a duplicate at that call site; it does not repair duplicates already in the array and does not make an Array behave like a Set. When uniqueness is the data model rather than a one-off guard, a Set expresses that rule directly.
Blueprint and C++ return different information
Blueprint's Add Unique returns -1 when the item was already present.
C++ TArray::AddUnique returns the element's index whether it was already
present or newly appended. Do not copy a Blueprint return-value test into
C++ unchanged.
Insert: choose the position and accept the shift¶
Insert's valid positions include the end:
Array Length = 3
valid Insert indices = 0, 1, 2, 3
Insert at 0 -> new first item
Insert at 2 -> new item before the old index 2
Insert at Length -> append at the end
Every old element at or after the insertion point receives a new index. Insert is therefore a poor fit when other systems treat positions as permanent IDs. Store a stable item ID inside each entry, or repair every dependent index after the mutation.
Remove Index and Remove Item answer different questions¶
Use Remove Index when position identifies the exact slot:
InventorySlots = [Potion, Potion, Key]
Remove Index(1)
-> [Potion, Key]
Use Remove Item when equality identifies everything to erase:
InventorySlots = [Potion, Potion, Key]
Remove Item(Potion)
-> [Key]
-> Return Value = true
Remove Item returns only whether one or more matches existed, not how many it removed. Compare Length before and after if the count matters, or write an explicit loop when your design should remove only one matching stack.
Both operations preserve the relative order of the remaining elements, but later indices shift left:
[A, B, C, D]
Remove Index(1)
-> [A, C, D]
1 2
Any saved selection index, Map from ID to array index, or widget-to-slot index at or after the removed position may now be stale.
Find and Contains use the same equality question¶
Find returns the first equal position:
Scores = [10, 20, 10]
Find Item(10) -> 0
Find Item(99) -> -1
Contains reduces that answer to membership:
Contains Item(10) -> true
Contains Item(99) -> false
Use Find when the index is the next input. Use Contains when the branch only
needs yes/no. Avoid Contains followed by Find; that scans twice. Find once,
then compare the result with -1 or guard it with Is Valid Index.
-1 is an invalid-index sentinel, not a valid array position and not the last
element. The array read/index page explains the distinction between
Length, Last Index, and Is Valid Index.
What “equal” means¶
All four equality-based nodes - Add Unique, Remove Item, Find Item, and Contains Item - ask the array element property whether two values are identical.
Primitive and value-like elements¶
- Integers and Booleans compare their values.
- Names, Strings, enums, Vectors, Rotators, and other supported value types use that type's equality rules.
- Floating-point equality is exact enough that calculated values which print the same may still differ in their stored bits. Use a loop plus Nearly Equal when a tolerance is the actual rule.
Struct elements¶
A typical Blueprint struct compares its reflected fields. If any compared field differs, the structs are not equal:
EntryA = { ItemId: Potion, Count: 1 }
EntryB = { ItemId: Potion, Count: 3 }
EntryA is not equal to EntryB
If the gameplay rule is “same ItemId regardless of Count,” Find Item and Add
Unique on the whole struct are the wrong tools. Loop and compare ItemId, or
maintain a Map keyed by ItemId. Native C++ structs can define specialized
identity behavior, so document it when a project type does not use ordinary
field equality.
Object-reference elements¶
Object references compare identity:
EnemyRefA and another reference to the same Enemy actor -> equal
Enemy1 and Enemy2 with identical variables and meshes -> not equal
The comparison does not compare display names, tags, transforms, or every
property on the objects. It also is not a lifetime check. A slot can hold
None, or still identify an object that is pending destruction. Contains may
truthfully say the stored reference value exists while Is Valid says the
object must no longer be used.
When an operation does nothing or reports failure¶
There is no exception or hidden failure event.
| Situation | Visible result | Mutation |
|---|---|---|
| Add Unique finds an equal item | returns -1 |
none |
| Find cannot find an equal item | returns -1 |
none; it is a read |
| Contains cannot find an equal item | returns false |
none; it is a read |
| Remove Item finds no equal item | returns false |
none |
| Remove Index receives a negative or out-of-range index | array warning in editor/development output; execution continues | none |
Insert receives an index outside 0..Length |
array warning in editor/development output; execution continues | none |
For Remove Index, a valid index is 0..Length - 1. For Insert, Length itself
is also valid because it means “insert after the current last element.” Guard a
dynamic removal index with Is Valid Index. Guard an insertion index with the
slightly different rule Index >= 0 AND Index <= Length.
Do not infer failure from a default-looking item value. Search operations give
you explicit -1 or Boolean results; use them.
Safe mutation while iterating¶
Removing during a forward walk can skip elements:
start: [A, B, C, D]
loop Index = 1, remove B
now: [A, C, D]
next loop Index = 2 -> visits D; C was skipped
Use one of these owned patterns.
Reverse index loop¶
for Index from Last Index down to 0:
if Items[Index] should be removed:
Remove Index(Items, Index)
Removing the current element cannot shift an unvisited lower index. Keep callbacks that can mutate the same array out of this loop, or re-check the index immediately before use.
Collect, then remove¶
PendingRemoval = []
For Each Items:
if Item should be removed:
Add Item to PendingRemoval
after the loop:
For Each PendingRemoval:
Remove Item from Items
This is clear when listeners, interfaces, or event dispatchers participate. Remember that Remove Item removes every equal occurrence.
Build a filtered replacement¶
KeptItems = []
For Each Items:
if Item should stay:
Add Item to KeptItems
after the loop:
Items = KeptItems
This makes the read phase stable and the one final mutation obvious. It also forces you to decide which order to preserve.
The patterns everyone actually uses¶
Add or stack an inventory entry¶
FoundIndex = Find entry whose ItemId equals IncomingItemId
if FoundIndex != -1 and item is stackable:
copy Entry at FoundIndex
change Count
Set Array Elem with the changed struct
else:
NewIndex = Add(new Entry)
Do not use Add Unique on the whole inventory struct when Count or durability can differ; whole-struct equality is not “same item definition.”
Track unique object references in an Array¶
AddedIndex = Add Unique(TrackedActors, Candidate)
if AddedIndex != -1:
initialize per-entry state
else:
actor was already tracked
If membership is the only reason for the collection, use a Set instead.
Clean stale Actor references¶
for Index from Last Index(TrackedActors) down to 0:
Actor = Get TrackedActors[Index]
if NOT Is Valid(Actor):
Remove Index(TrackedActors, Index)
Find/Contains alone cannot replace the Is Valid check.
C++ twins¶
Original example code:
const int32 NewIndex = Items.Add(NewItem);
const int32 ExistingOrNewIndex = Items.AddUnique(NewItem);
const int32 FirstIndex = Items.Find(ItemToFind); // INDEX_NONE when absent
const int32 RemovedCount = Items.Remove(ItemToRemove); // removes all equals
if (Items.IsValidIndex(Index))
{
Items.RemoveAt(Index);
}
C++ returns richer information in two places: TArray::AddUnique gives the
existing-or-new index, and TArray::Remove gives the number removed. Blueprint
Add Unique instead uses -1 for “already present,” and Blueprint Remove Item
reduces the removed count to a Boolean.
What these nodes do not do¶
They do not:
- make array indices into stable IDs;
- sort the array after a mutation;
- remove only the first equal item with Blueprint Remove Item;
- compare two object references by their fields, tags, or visual appearance;
- apply a tolerance to floating-point equality;
- validate the lifetime of an object reference;
- repair duplicates already present when Add Unique is called;
- make forward-loop removal safe; or
- replicate, save, or notify UI about the mutation by themselves.
Those last effects belong to the system that owns the array: its replicated
property, SaveGame copy, and OnInventoryChanged-style event.
Lookalikes - which one do I want?¶
| Node or container | Use when | Watch out |
|---|---|---|
| Add | Append one item and allow duplicates. | Returns a new index; later mutations can move it. |
| Add Unique | Append only when no equal item exists. | Returns -1 for an existing item; whole-element equality may be broader than your gameplay key. |
| Insert | Position is meaningful and later items should shift. | Valid range includes Length; dependent indices become stale. |
| Remove Index | You know the exact slot to remove. | Guard the current index; later indices shift. |
| Remove Item | Remove every element equal to a value. | Boolean says any removed, not how many. |
| Find Item | You need the first matching index. | -1 means missing; it is a linear search. |
| Contains Item | You need only a membership Boolean. | Do not call Find afterward and scan twice. |
| Set | Unique membership is the collection's core rule. | No stable order or duplicate entries. |
| Map | A stable key should retrieve or replace one value. | Keys are unique and iteration order is not presentation order. |
| Set Array Elem | Replace one known array slot. | Struct copies must be written back; index policy still matters. |
Rule of thumb: Add for another entry, Add Unique for a guarded append, Insert for a chosen position, Remove Index for one slot, Remove Item for every equal value, Find for a position, and Contains for yes/no.
Going deeper¶
- Array Get / Length / Last Index / Is Valid Index - safe reads, copy/reference behavior, and empty-array boundaries.
- Arrays, Sets, Maps, and ForEach - choosing the container shape before choosing a node.
- Add, Remove, Stack, and Broadcast Inventory Changes - the larger inventory contract around these primitive mutations.
- Blueprint variables and references - value copies versus references to live objects.
- Is Valid - object lifetime, not array membership or index bounds.
- Official docs: Add, Insert, Remove Index, Contains Item, and Array Containers.
- Engine source (requires engine access - we do not reproduce it here):
UKismetArrayLibrary::GenericArray_Add,GenericArray_AddUnique,GenericArray_Insert,GenericArray_Remove,GenericArray_RemoveItem, andGenericArray_FindinEngine/Source/Runtime/Engine/Private/KismetArrayLibrary.cpp; Blueprint declarations and return contracts inEngine/Source/Runtime/Engine/Classes/Kismet/KismetArrayLibrary.h; nativeTArraybehavior inEngine/Source/Runtime/Core/Public/Containers/Array.h.