Skip to content

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 == Length is a valid append; the valid insertion range is 0..Length.
  • Remove Index removes exactly one position. Remove Item removes every equal occurrence and returns true if at least one was removed.
  • Find Item returns the first equal item's index, or -1 when 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, and Add Unique do not prove an object reference is usable. An array can contain None or 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