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
falseand 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
trueonly when an entry was actually removed. Removing a missing entry quietly returnsfalse. - 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¶
- Arrays, Sets, Maps, and ForEach - choosing the container that matches the data model.
- Array Add / Add Unique / Insert / Remove / Find / Contains - the ordered, duplicate-friendly mutation contracts.
- Blueprint variables and references - value copies, object references, and durable SaveGame data.
- Add, Remove, Stack, and Broadcast Inventory Changes - keyed counts, array slots, writeback, and one post-mutation event.
- Replication basics - owning replicated state and keeping server authority separate from local container edits.
- Create / Save / Load Game to Slot - copying stable data into a SaveGame object.
- Official docs: Set Containers, Map Containers, Set Remove, and Map Remove.
- Engine source (requires engine access - we do not reproduce it here):
UBlueprintSetLibrary::GenericSet_Add,GenericSet_Contains,GenericSet_Remove, andGenericSet_ClearinEngine/Source/Runtime/Engine/Private/BlueprintSetLibrary.cpp;UBlueprintMapLibrary::GenericMap_Add,GenericMap_Find,GenericMap_Remove, andGenericMap_ClearinEngine/Source/Runtime/Engine/Private/BlueprintMapLibrary.cpp; declarations inEngine/Source/Runtime/Engine/Classes/Kismet/BlueprintSetLibrary.handBlueprintMapLibrary.h; native container rules inEngine/Source/Runtime/Core/Public/Containers/Set.handMap.h.