What a set is
A vector keeps every value you put in it, repeats included. Sometimes you only need to know which values exist. A set is made for that: it holds each value once, and inserting a value that is already there changes nothing. C++'s hash-based set is unordered_set.
unordered_set<string> pets = {"cat", "dog", "cat"}; // cat goes in once
pets.insert("fish"); // a new value
pets.insert("dog"); // already in it, no change
cout << pets.size() << endl; // 3
cout << pets.count("dog") << endl; // 1, so dog is in it
The operations
unordered_set<string> s;makes an empty set of strings.s.insert(x)putsxin the set.s.count(x)is 1 whenxis in the set and 0 when it isn't. A set holds each value once, so it never gives more than 1.s.size()is the number of values it holds.unordered_set<string> s(v.begin(), v.end());builds a set from a vectorv. The repeats go in once.
A lookup with count is fast even for a big set, because it jumps to where the value would be instead of looking at the values one by one, as a search in a vector does.
Why not just use a vector?
Take vector<int> v = {4, 8, 4, 15, 8, 4};. Building unordered_set<int> distinct(v.begin(), v.end()); gives a set whose size() is 3, while v.size() is still 6. To ask "is 15 in here?" with a vector you would look at the values one by one; the set answers with a single count.
Strings in a set are compared by their characters, not by where they sit in memory, so "cat" typed twice is the same value. Case matters, though: "Cat" and "cat" are two different values, and a set would hold both.
Traps
- No
containsin C++17. That member only arrived in C++20, so the lesson testss.count(x) > 0. Callingcontainsin C++17 gives the error "has no member named 'contains'". - No indexes and no order.
s[0]doesn't compile (there is nooperator[]for a set). To use every value, loop over the set; the order is not fixed. insertsays whether it added. It returns a pair whosesecondistruewhen the value was new andfalsewhen it was already there. Inserting"pen"into an empty set givestrue, and inserting it again givesfalsewith the size still 1. The lesson doesn't need it, but it is how you can tell.
The lesson's task
The vector names is {"Ava", "Leo", "Ava", "Mia", "Leo"}: Ava and Leo each appear twice. The task builds kids from it, then signs up Zoe and Ava again, then checks for Leo.
- Build
kidsfromnames.begin()andnames.end(). The set holds Ava, Leo and Mia, so its size is 3. - Insert
"Zoe"and then"Ava". Zoe is new, and Ava is already in the set, so the size becomes 4, not 5. - If
kids.count("Leo") > 0, printLeo is coming.
class Solution {
public:
void mainFunction() {
vector<string> names = {"Ava", "Leo", "Ava", "Mia", "Leo"};
unordered_set<string> kids(names.begin(), names.end());
cout << kids.size() << " kids" << endl;
kids.insert("Zoe");
kids.insert("Ava");
cout << kids.size() << " kids" << endl;
if (kids.count("Leo") > 0) {
cout << "Leo is coming" << endl;
}
}
};
The output is:
3 kids
4 kids
Leo is coming
Key takeaways
- A set keeps each value once; repeats are ignored.
insertadds,counttests (1 or 0),sizecounts.- Build a set from a vector with
(v.begin(), v.end())to drop its repeats. - In C++17 use
count(x) > 0, notcontains.
