Skip to content

Set and Map Add / Find / Contains / Remove / Clear

At a glance

Lives in: Blueprint Set Library and Blueprint Map Library; Blueprint API / Utilities / Set and Map - Set: stores unique elements; Contains and Remove return Booleans - Map: stores one value per unique key; Find returns a value plus a Found Boolean, and Remove returns a Boolean - Missing entries: report false without an exception or warning - Official docs: Blueprint Set Library, Blueprint Map Library, Map Add, and Map Find

The one-minute version

  • A Set stores each equal element at most once. Add an existing element and nothing changes. The Blueprint Add node has no “newly added” output; branch on Contains first when first-time behavior matters.
  • A Map stores one value for each unique key. Add a key that already exists and the new value replaces the old value. The current Blueprint Add node has no “inserted versus replaced” output.
  • Set Contains asks whether an equal element is present. Map Contains asks whether a key is present. Both return a Boolean and do not mutate.
  • Map Find returns the value associated with a key and a Boolean. When the key is missing, the Boolean is false and the Value pin is a default-constructed value. Test the Boolean; 0, false, None, or an empty struct may also be a legitimate stored value.
  • Set and Map Remove return true only when an entry was actually removed. Removing a missing entry quietly returns false.
  • Clear empties the container. It does not destroy referenced Actors or Widgets, save the change, replicate it, or broadcast a gameplay event.
  • Set elements and Map keys use both hashing and equality. Object references mean “this exact object,” not “another object with the same fields.”
  • Do not use Set or Map iteration order for inventory slots, menus, save-file display, or deterministic choices. Build and sort an Array by a stable key.
  • A copied struct value must be written back. A copied object reference still points to the same object and still needs Is Valid.

Set and Map solve different lookup problems

Use a Set when the value itself is the membership question:

Set CurrentTargets:
    EnemyA
    EnemyB

Contains EnemyA -> true

Use a Map when one value should be found through another value:

Map ItemCounts:
    Potion -> 3
    Key    -> 1

Find Potion -> Value 3, Found true

A Set has no separate Blueprint Find-value operation because a successful lookup would only give you the same element you searched for. Contains is its normal lookup. A Map needs Find because the key and value are different types or different roles.

Operation Set result Map result
Add new stores the element; no data return stores key/value; no data return
Add existing no change replaces the value associated with the equal key
Contains true if an equal element exists true if an equal key exists
Find no ordinary value lookup; use Contains Value copy + true, or default value + false
Remove removes the equal element; returns whether it existed removes key and its value; returns whether key existed
Clear Length becomes 0 Length becomes 0

What the nodes actually do

pseudocode of the Blueprint behavior - not engine source

function SetAdd(Set, Item):
    Hash = hash(Item)
    if no equal Item exists in Hash's candidate group:
        store Item

function MapAdd(Map, Key, Value):
    Hash = hash(Key)
    if an equal Key exists in Hash's candidate group:
        replace that entry's Value
    else:
        store a new Key -> Value pair

function MapFind(Map, Key):
    find an entry with matching hash and equal Key
    if found:
        return copy of Value, true
    return default-constructed Value, false

Hashing narrows the candidates; equality decides whether a candidate is the same Set element or Map key. Different values are allowed to share a hash - the equality check separates them. Values that compare equal must produce the same hash or the container cannot reliably find what it stored.

Blueprint-supported element/key types provide these rules through Unreal's property system. A custom C++ key type must provide compatible equality and GetTypeHash behavior. If Blueprint will not offer a type as a Set element or Map key, use an intentional stable key such as a Name, enum, Gameplay Tag, or object reference rather than forcing display Text or mutable data into the role.

Add means “unique” for a Set and “upsert” for a Map

Set Add

CurrentTargets = { EnemyA, EnemyB }

Add EnemyA -> CurrentTargets is unchanged
Add EnemyC -> CurrentTargets now has three elements

Set Add has no Boolean return in Blueprint. If new membership triggers work, make the branch explicit:

if NOT Contains(CurrentTargets, Candidate):
    Add Candidate
    run first-added behavior

That performs a membership lookup before Add's own lookup, but it is the clear Blueprint pattern when “first time” is meaningful. If you only need the final membership, call Add directly and accept its idempotent behavior.

Map Add

Map Add is an insert-or-replace operation, often called an upsert:

ItemCounts = { Potion -> 2, Key -> 1 }

Add Potion -> 5
ItemCounts = { Potion -> 5, Key -> 1 }
Length is still 2

This replacement is useful for state snapshots and struct writeback. It is dangerous when “duplicate key” should be an error. Preserve an existing value by branching on Contains or Find before Add:

Find Settings by SettingName
-> Found true: own the duplicate-key policy
-> Found false: Add SettingName -> NewValue

The current Blueprint Map Add node has only execution output. It does not tell you whether it inserted or replaced, and it does not merge struct fields or increment numeric values automatically.

Find and Contains: branch on the result, not the value

Map Find has two outputs:

Find(ItemCounts, Potion)
-> Value = 3
-> Return Value / Found = true

Find(ItemCounts, Torch)
-> Value = 0       // default Integer
-> Return Value / Found = false

0 cannot be the failure test because Potion -> 0 is a valid stored pair. The same ambiguity applies to every default-looking value:

Map value type Missing Find output can look like Why that is not proof of failure
Integer / Float 0 zero can be stored intentionally
Boolean false false can be the setting's real value
Name None the None Name can be stored
Object reference None a key can deliberately map to an empty reference
Struct default fields a default struct can be a real value

Always branch on Find's Boolean when you need the value. Use Contains only when the value is irrelevant; Contains followed by Find repeats the key lookup.

Set Contains and Map Contains are pure reads. They do not insert a default, repair a stale object reference, or make the result remain true after another execution path mutates the container.

Remove and Clear are quiet mutations

For a Set:

Remove(CurrentTargets, EnemyA) -> true
Remove(CurrentTargets, EnemyZ) -> false

For a Map:

Remove(ItemCounts, Potion) -> true
// both the Potion key and its associated count leave the Map

Remove(ItemCounts, Torch) -> false

Missing entries are normal and produce no exception, failure execution pin, or warning. Use the Boolean only when the distinction matters.

Clear removes every stored Set element or Map key/value pair and leaves Length 0. Clearing references removes those references from this container; it does not call Destroy Actor, Remove from Parent, or any custom cleanup on the objects. Other owners may still reference them.

Neither Remove nor Clear calls your inventory/widget/gameplay dispatcher. The container's owner must broadcast after the complete mutation if listeners need to refresh.

Equality and hashing in beginner terms

Primitive and value keys

Integers, Booleans, Names, enums, Strings, and other supported value-like types follow their type's equality and hash rules. Floating-point keys are usually a poor design: values produced by calculations may be almost equal but not exactly equal, and Set/Map lookup does not apply a “Nearly Equal” tolerance. Quantize the value or use a stable discrete ID.

Struct elements and keys

When the editor permits a struct in the Set/key role - or when C++ supplies the required hash - a typical reflected struct's identity includes its compared fields. These are different whole values:

{ ItemId: Potion, Count: 1 }
{ ItemId: Potion, Count: 2 }

If the gameplay key is only ItemId, make the Map key ItemId; do not use the whole mutable inventory entry as the key. Native C++ structs used as keys need an equality rule and hash that agree about exactly which fields form identity.

Object references

Object-reference Sets and Map keys use object identity:

two references to EnemyA -> equal key/element
EnemyA and EnemyB with matching health and mesh -> different key/element

Changing EnemyA's health does not turn it into a different object key. Destroying EnemyA also does not automatically remove its stored reference. Contains can still answer a reference-value question while Is Valid answers the separate lifetime question. Clean stale references according to the system that owns the container.

Copies, references, and writing a changed value back

Map Find returns a value output suitable for Blueprint use. Treat value-like outputs as copies:

Map ItemEntries: Name -> FInventoryEntry

Find(ItemEntries, Potion)
-> EntryCopy
-> change EntryCopy.Count
-> Add(ItemEntries, Potion, EntryCopy)  // replace stored value

Changing EntryCopy without the final Add does not update the Map.

For an object-reference value, the copied value is another reference to the same object:

Map WidgetsByName: Name -> UserWidget reference

Find(WidgetsByName, Inventory)
-> WidgetRef points to the same Widget object stored in the Map

Calling a function through a valid WidgetRef affects that Widget. Reassigning the local reference does not replace the Map value; Map Add does. The object can still be None or become invalid after the lookup.

Set-to-Array, Map Keys, and Map Values likewise create arrays of values or reference values. They do not create clones of referenced Actors, Components, or Widgets.

Order is not part of the contract

Sets and Maps are hash containers backed by sparse storage. Their internal positions can change as entries are added, removed, or storage grows. Do not treat observed editor or runtime order as insertion order.

Wrong presentation pattern:

Map Keys -> For Each -> add menu row
// menu order can change as the Map changes

Owned presentation pattern:

Map Keys -> Array
Sort by stable Name / enum rank / explicit DisplayOrder
For Each sorted key:
    Find value
    build row

If two peers must make the same gameplay choice, do not choose “the first Set element” or “the first Map key.” Sort by a shared stable key, or use an ordered Array whose order is part of the replicated/saved state.

Replication, saving, and validity are separate decisions

Container nodes only change the variable in the current object on the current machine.

  • Replication: the server-authoritative Actor or Actor Component, the variable's replication setup, and the client/server mutation path decide what other machines observe. Calling Add on a client does not become a Server RPC and does not authorize that client to change shared state.
  • SaveGame: copy durable values into a SaveGame object's properties and run the save flow. Runtime Actor, Component, and Widget references are not durable save identities; store stable IDs and rebuild references after load.
  • Presentation order: build a sorted Array explicitly. Replicating or loading the same Set/Map entries does not grant them a stable UI order.
  • Object validity: run Is Valid before using stored object references and remove stale entries according to ownership/lifetime events.

These concerns belong to the system around the container, not to Add, Find, Remove, or Clear.

The patterns everyone actually uses

Unique “already processed” membership

if NOT Contains(ProcessedActors, HitActor):
    Add HitActor to ProcessedActors
    apply one-time effect

Clear the Set when a new attack/pulse begins. Validate HitActor before using it; membership and lifetime are different.

Add or increment a Map count

Find ItemCounts by ItemId
-> Found true:
       Add ItemCounts (ItemId -> Value + Amount)  // replaces old value
-> Found false:
       Add ItemCounts (ItemId -> Amount)

One Find gives both existence and the current value; a preceding Contains is unnecessary.

Remove and broadcast once

Removed = Remove ItemCounts by ItemId

if Removed:
    Call OnInventoryChanged

If several mutations form one transaction, make them all first and broadcast once after the final state is consistent.

Update a stored struct

Find Settings by SettingId
-> Found true:
       modify returned struct copy
       Add SettingId -> ModifiedCopy
-> Found false:
       own the missing-setting path

C++ twins

Original example code:

TSet<TObjectPtr<AActor>> ProcessedActors;
const bool bAlreadyTracked = ProcessedActors.Contains(HitActor);
if (!bAlreadyTracked && IsValid(HitActor))
{
    ProcessedActors.Add(HitActor);
}
const int32 RemovedCount = ProcessedActors.Remove(HitActor);

TMap<FName, FInventoryEntry> EntriesById;
EntriesById.Add(ItemId, NewEntry); // replaces the value for an existing key

if (FInventoryEntry* Entry = EntriesById.Find(ItemId))
{
    ++Entry->Count; // C++ Find returns a pointer to stored value
}

EntriesById.Remove(ItemId);
EntriesById.Reset();

Blueprint Map Find gives a value output plus a Boolean and a value-like result must be written back. C++ TMap::Find returns a pointer to the stored value or nullptr, so non-const C++ can edit the stored value through that pointer.

What these nodes do not do

They do not:

  • preserve Set or Map insertion order;
  • merge an existing Map value with the new value;
  • report “new versus already present” from Blueprint Set Add;
  • report “inserted versus replaced” from current Blueprint Map Add;
  • distinguish a missing Map key by inspecting only Find's Value pin;
  • compare object references by object fields;
  • keep referenced objects alive and usable forever;
  • destroy objects when references are removed or cleared;
  • replicate client mutations, save to disk, sort UI, or notify listeners; or
  • make a mutable/display value into a good stable key.

Lookalikes - which one do I want?

Tool Use when Watch out
Set Add / Contains / Remove One value is either a unique member or not. No stable order; Add has no newly-added output in Blueprint.
Map Add / Find / Remove A unique key owns one replaceable value. Add overwrites; branch on Find's Boolean.
Array Position, duplicates, or authored order matters. Membership and keyed lookup are linear unless you add another structure.
Array Add Unique You still need Array order but want guarded appends. Existing duplicates remain; Blueprint returns -1 when already present.
Find Component by Tag You need one Actor Component selected by class plus a plain Name tag. Component order still decides among multiple matching tags.
Data Table Stable authored rows share one struct schema. It is asset data, not a mutable per-instance Map.

Rule of thumb: Set for unique membership, Map for key-to-value lookup, Array for order or duplicates.

Going deeper