언리얼 C++ 프로그래밍

[언리얼 C++] 컨테이너 클래스 TMap 사용법(쿼리, 엘리먼트 제거, 소팅)

언린이 2021. 7. 11. 15:36

TMap 컨테이너 클래스의 기본적인 설명과 간단한 사용법은 [언리얼 C++] 컨테이너 클래스 TMap 사용법(생성 및 삽입, 반복처리) (tistory.com) 글을 참고하시기 바랍니다.

이 글에서는 TMap 컨테이너 클래스의 쿼리, 엘리먼트 제거, 소팅 기능에 대해 알아보겠습니다.

 

 

1. TMap 컨테이너 쿼리 기능

int32 Count = FruitMap.Num(); // Count = 6

현재 TMap 컨테이너에 있는 엘리먼트 개수를 알아내려면 Num() 함수를 호출하면 됩니다.

 

bool bHas7 = FruitMap.Contains(7); // bHas7 = true
bool bHas8 = FruitMap.Contains(8); // bHas8 = false

TMap 컨테이너에 특정 Key 존재 여부를 알아내기 위해서는 Contains() 함수를 호출하시면 됩니다.

 

FString Val7 = FruitMap[7]; // Val7 = "Pineapple"
FString Val8 = FruitMap[8]; // 어서트!

TMap 컨테이너에 특정 Key가 존재한다는 것을 안다면, Key를 인덱스로 하여 operator[]를 사용하여 해당 값을 조회하시면 됩니다. non-const TMap 컨테이너는 non-const 레퍼런스를, const TMap 컨테이너는 const 레퍼런스를 반환합니다.

TMap 컨테이너에 Key가 존재하는지 확실하지 않다면, Contains() 함수에 operator[]를 사용하면 됩니다. 그러나 이는 이상적이지 않은데, 불러오기에 성공해도 같은 키를 두번 조회해야 하기 때문입니다.

이를 위해서 Find() 함수를 제공해주고 있습니다.

 

FString* Ptr7 = FruitMap.Find(7); // *Ptr7 = "Pineapple"
FString* Ptr8 = FruitMap.Find(8); //  Ptr8 = nullptr

Find() 함수는 Contains() 함수에 operator[]를 사용하는 동작을 한번의 조회로 합칩니다.

Find() 함수는 TMap 컨테이너에 Key가 존재한다면 엘리먼트 값으로의 포인터를 반환하고, 존재하지 않다면 널 포인터를 반환합니다.

Find() 함수를 const TMap 컨테이너에 호출하면 반환하는 포인터도 const가 됩니다.

 

FString& Ref8 = FruitMap.FindOrAdd(8);
// Ref8     = ""
// FruitMap = {
//  { Key: 5, Value: "Mango"     },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" },
//  { Key: 4, Value: "Kiwi"      },
//  { Key: 3, Value: "Orange"    },
//  { Key: 9, Value: "Melon"     },
//  { Key: 8, Value: ""          }
// }

FString Val7 = FruitMap.FindRef(7);
FString Val6 = FruitMap.FindRef(6);
// Val7     = "Pineapple"
// Val6     = ""
// FruitMap = {
//  { Key: 5, Value: "Mango"     },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" },
//  { Key: 4, Value: "Kiwi"      },
//  { Key: 3, Value: "Orange"    },
//  { Key: 9, Value: "Melon"     },
//  { Key: 8, Value: ""          }
// }

쿼리 결과 유효성을 보장하려면, FindOrAdd() 함수 또는 FindRef() 함수를 사용하면 됩니다.

FindOrAdd() 함수는 제공한 Key에 연관된 값으로의 레퍼런스를 반환합니다.

Key가 TMap 컨테이너에 존재하지 않는 경우, FindOrAdd() 함수는 새로 생성된 엘리먼트에 Key와 기본 생성된 값을 반환하고, TMap 컨테이너에 추가합니다. 이 행위는 TMap 컨테이너를 수정할 수도 있기 때문에, non-const TMap 컨테이너에서만 사용할 수 있습니다.

FindRef() 함수는 그 이름과 달리 Key에 연관된 값을 반환하고, TMap 컨테이너에 해당 Key가 존재하지 않는 경우 기본 생성된 값 사본을 반환합니다. FindRef() 함수는 새 엘리먼트를 생성하지 않으므로 const 및 non-const TMap 컨테이너 양쪽에서 사용이 가능합니다.

FindOrAdd() 함수와 FindRef() 함수는 TMap 컨테이너에서 Key를 찾지 못해도 성공하므로, 미리 Contains() 함수를 사용한 검사나 반환 값의 null 검사와 같은 통상의 안전 확인 절차 없이 호출해도 안전합니다.

 

const int32* KeyMangoPtr   = FruitMap.FindKey(TEXT("Mango")); // *KeyMangoPtr   = 5
const int32* KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat")); //  KeyKumquatPtr = nullptr

FindKey() 함수는 역조회를 하여 제공된 Value에 일치하는 Key를 검색하여 해당 Key로의 포인터를 반환합니다.

TMap 컨테이너에 존재하지 않는 Value를 검색하면 널 포인터를 반환합니다.

 

TArray<int32>   FruitKeys;
TArray<FString> FruitValues;
FruitMap.GenerateKeyArray  (FruitKeys);
FruitMap.GenerateValueArray(FruitValues);
// FruitKeys   = { 5,2,7,4,3,9,8 }
// FruitValues = { "Mango","Pear","Pineapple","Kiwi","Orange","Melon","" }

GenerateKeyArray() 함수와 GenerateValueArray() 함수는 파라미터로 넘겨받은 TArray 컨테이너를 각각 모든 Key/Value의 사본으로 채웁니다.

두 경우 모두, 전달되는 컨테이너는 채워지기 전에 비워지므로, 엘리먼트의 최종 개수는 TMap 컨테이너의 엘리먼트 개수와 항상 동일합니다.

 

 

2. TMap 컨테이너 엘리먼트 제거 기능

FruitMap.Remove(8);
// FruitMap = {
//  { Key: 5, Value: "Mango"     },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" },
//  { Key: 4, Value: "Kiwi"      },
//  { Key: 3, Value: "Orange"    },
//  { Key: 9, Value: "Melon"     }
// }

Remove() 함수에 제거할 엘리먼트의 Key를 넣어주는 것으로 TMap 컨테이너에서 엘리먼트를 제거할 수 있습니다.

반환 값은 제거된 엘리먼트의 개수이며, TMap 컨테이너에 Key와 일치하는 엘리먼트가 존재하지 않는 경우 0을 반환할 수도 있습니다.

 

FString Removed7 = FruitMap.FindAndRemoveChecked(7);
// Removed7 = "Pineapple"
// FruitMap = {
//  { Key: 5, Value: "Mango"  },
//  { Key: 2, Value: "Pear"   },
//  { Key: 4, Value: "Kiwi"   },
//  { Key: 3, Value: "Orange" },
//  { Key: 9, Value: "Melon"  }
// }

FString Removed8 = FruitMap.FindAndRemoveChecked(8); // 어서트!

FindAndRemoveChecked() 함수는 TMap 컨테이너에서 엘리먼트를 제거하고 해당 Value를 반환하는데 사용됩니다.

이름의 "Checked" 부분은 Key가 존재하지 않으면 언리얼 엔진에서 어서트에 해당하는 check 매크로를 호출함을 나타냅니다.

 

FString Removed;
bool bFound2 = FruitMap.RemoveAndCopyValue(2, Removed);
// bFound2  = true
// Removed  = "Pear"
// FruitMap = {
//  { Key: 5, Value: "Mango"  },
//  { Key: 4, Value: "Kiwi"   },
//  { Key: 3, Value: "Orange" },
//  { Key: 9, Value: "Melon"  }
// }

bool bFound8 = FruitMap.RemoveAndCopyValue(8, Removed);
// bFound8  = false
// Removed  = "Pear"
// FruitMap = {
//  { Key: 5, Value: "Mango"  },
//  { Key: 4, Value: "Kiwi"   },
//  { Key: 3, Value: "Orange" },
//  { Key: 9, Value: "Melon"  }
// }

RemoveAndCopyValue() 함수는 Remove() 함수와 비슷하지만, 제거된 엘리먼트의 Value를 파라미터로 넘겨준 레퍼런스에 복사합니다. 지정한 Key가 TMap 컨테이너에 존재하지 않으면, 파라미터로 넘겨준 레퍼런스는 변경되지 않으며 함수는 false를 반환합니다.

 

FruitMap.Empty(); // 여기서 Reset()을 호출해도 됩니다.
// FruitMap = {}

마지막으로, Empty() 함수 또는 Reset() 함수로 TMap 컨테이너의 모든 엘리먼트들을 제거할 수 있습니다.

Empty() 함수와 Reset() 함수는 비슷하지만, Empty() 함수는 TMap 컨테이너에 남겨둘 슬랙 양을 지정할 수 있는 반면 Reset() 함수는 그럴 수 없습니다.

 

 

3. TMap 컨테이너 소팅 기능

FruitMap.KeySort([](int32 A, int32 B) {
    return A > B; // sort keys in reverse
});
// FruitMap = {
//  { Key: 9, Value: "Melon"  },
//  { Key: 5, Value: "Mango"  },
//  { Key: 4, Value: "Kiwi"   },
//  { Key: 3, Value: "Orange" }
// }

FruitMap.ValueSort([](const FString& A, const FString& B) {
    return A.Len() < B.Len(); // sort strings by length
});
// FruitMap = {
//  { Key: 4, Value: "Kiwi"   },
//  { Key: 5, Value: "Mango"  },
//  { Key: 9, Value: "Melon"  },
//  { Key: 3, Value: "Orange" }
// }

TMap 컨테이너는 소팅이 가능합니다. 소팅 이후 TMap 컨테이너를 반복처리하면 엘리먼트가 소팅된 순서대로 나오지만, 이 순서는 TMap 컨테이너를 수정하면 보장되지 않습니다.

소팅은 불안정하므로, TMultiMap 컨테이너의 엘리먼트가 어떤 순서로 나올지는 알 수 없습니다.

KeySort() 함수 또는 ValueSort() 함수를 사용하여 각각 Key/Value 소팅이 가능하며, 두 함수 모두 소팅 순서를 나타내는 이항 술부를 파라미터로 넘겨받습니다.