Insert a pair into a map and increase the count? - c++

The codebase I'm working in uses the map::operator[] to insert and increment the count of items in that entry by one (this is a knowledge gap for me). Here's an example:
map<string, size_t> namesMap;
namesMap[firstName]++;
What I want to do is tack on an ID to the insert while retaining the increment behavior in the syntax above.
My new map would look like this:
map<string, pair<int, size_t>> namesMapWithID;
I'm struggling to see how to get the equivalent functionality with my new map. This is basically my goal (obviously wrong since "++" cannot be used this way):
namesMapWithID.insert(firstName, make_pair(employeeID, ++));
Is there a better approach that I'm missing?

You can do this via using the insert method along with the it/bool pair it returns, thereby delivering a single lookup (by name), setting the employee id if on the initial lookup, and then incrementing the counter respectively.
Something like this:
auto pr = namesMapWithID.insert(std::make_pair(firstName,
std::make_pair(employeeID, size_t())));
++pr.first->second.second;

Related

std::unordered_set::find - construct an instance only for find()

A lot of times I see my key is actually inside my value.
For example:
struct Elem {
int key;
// ... Other variables ...
}
That makes me want to use std::unordered_set instead of std::unordered_map, because I already have the key stored inside my value - no need to waste more place for std::unordered_map's .first (key).
Then I start implementing with std::unordered_set and get to the place I need to perform a find() over my std::unordered_set.
Then I realize I need to create an empty-shell Elem so I would be able to find(), beacuse std::unordered_set::find gets a Key for input
template < class Key, // unordered_set::key_type/value_type
class Hash = hash<Key>, // unordered_set::hasher
class Pred = equal_to<Key>, // unordered_set::key_equal
class Alloc = allocator<Key> // unordered_set::allocator_type
> class unordered_set;
Sometimes building an empty-shell Elem is hard / wasteful / maybe even not possible?
For example, when my key/value is
An iterator
A reference
A class with specific c'tor (not constructing the instance only with the key)
Q. Am I missing something?
Q. Is there a way to do find() that isn't wasteful? I mean that doesn't make me create an instance I didn't want to
Something really strange to me - that I already should have the element I'm looking for in order to find it, or at least an empty-shell of it.
When choosing a data structure to hold your data you need to consider your use case.
If you want to look up data from a key you should use a map. If you just want to store unique values in a collection and you don't need to look them up use set.
I don't see why its so much trouble to insert a element as map.emplace_back(elem.key, elem) vs set.emplace_back(elem) if it means that down the road you can just query the elem as map.at(key) or map[key] vs having create an empty elem.
Besides, std::set does the whole key thingamajig (roughly) underwater anyway. (source: What is the difference between set vs map in C++?)

STL map like data structure to allow searching based on two keys

I want to make a map like structure to allow searching by two keys both will be strings, here's an example:
Myclass s;
Person p = s.find("David"); // searching by name
// OR
p = s.find("XXXXX"); // searching by ID
i don't want a code solution, i just want some help to get started like the structures i can use to achieve what i want, help is appreciated guys, it's finals week.
Put your records into a vector (or list). Add a pointer to the record objects to two maps, one with one key and one with the other.
There are many different ways how this could be achieved. The question is: what are the complexities of insert, delete and lookup operations that you aim for?
std::map is implemented as red-black tree that provides increadibly quick self-balancing (rotations) and all of mentioned operations (lookup/find, insert, delete) with complexity of O(log(n)). Note that this suits the idea of single key.
With 2 keys you can not keep elements sorted because the order based on one key will be most likely different than order based on the other one. The most straightforward and natural approach would be storing records in one container and holding the keys used by this container in 2 different structures, one optimized for retrieving this key given id and the other one for retrieving it given name.
If there is a constraint of storing everything at one place while you'd like to optimize find operation that will support two different keys, then you could create a wrapper of std::map<std::string, Person> where each element would be contained twice (each time under a different key), i.e. something like:
std::map<std::string, Person> myContainer;
...
Person p;
std::string id = "1E57A";
std::string name = "David";
myContainer[id] = p;
myContainer[name] = p;
I can think of 2 advantages of doing this:
quite satisfying performance:
lookup with complexity O(log(2*n))
insertion & deletion with complexity O(2*log(2*n))
extremely simple implementation (using existing container)
you just need to remember than the "expected" size of the container is half of its actual size
both of the keys: id and name should be attributes of Person so that when you find a concrete element given one of these keys, you immediately have the other one too
Disadvantage is that it will consume 2x so much memory and there might even be a constraint that:
none of the names should be an id of some other person at the same time and vice versa (no id should be a name of some other person)

Address of map value

I have a settings which are stored in std::map. For example, there is WorldTime key with value which updates each main cycle iteration. I don't want to read it from map when I do need (it's also processed each frame), I think it's not fast at all. So, can I get pointer to the map's value and access it? The code is:
std::map<std::string, int> mSettings;
// Somewhere in cycle:
mSettings["WorldTime"] += 10; // ms
// Somewhere in another place, also called in cycle
DrawText(mSettings["WorldTime"]); // Is slow to call each frame
So the idea is something like:
int *time = &mSettings["WorldTime"];
// In cycle:
DrawText(&time);
How wrong is it? Should I do something like that?
Best use a reference:
int & time = mSettings["WorldTime"];
If the key doesn't already exist, the []-access will create the element (and value-initialize the mapped value, i.e. 0 for an int). Alternatively (if the key already exists):
int & time = *mSettings.find("WorldTime");
As an aside: if you have hundreds of thousands of string keys or use lookup by string key a lot, you might find that an std::unordered_map<std::string, int> gives better results (but always profile before deciding). The two maps have virtually identical interfaces for your purpose.
According to this answer on StackOverflow, it's perfectly OK to store a pointer to a map element as it will not be invalidated until you delete the element (see note 3).
If you're worried so much about performance then why are you using strings for keys? What if you had an enum? Like this:
enum Settings
{
WorldTime,
...
};
Then your map would be using ints for keys rather than strings. It has to do comparisons between the keys because I believe std::map is implemented as a balanced tree. Comparisons between ints are much faster than comparisons between strings.
Furthermore, if you're using an enum for keys, you can just use an array, because an enum IS essentially a map from some sort of symbol (ie. WorldTime) to an integer, starting at zero. So then do this:
enum Settings
{
WorldTime,
...
NumSettings
};
And then declare your mSettings as an array:
int mSettings[NumSettings];
Which has faster lookup time compared to a std::map. Reference like this then:
DrawText(mSettings[WorldTime]);
Since you're basically just accessing a value in an array rather than accessing a map this is going to be a lot faster and you don't have to worry about the pointer/reference hack you were trying to do in the first place.

C++: insert into std::map without knowing a key

I need to insert values into std::map (or it's equivalent) to any free position and then get it's key (to remove/modify later). Something like:
std::map<int, std::string> myMap;
const int key = myMap.insert("hello");
Is it possibly to do so with std::map or is there some appropriate container for that?
Thank you.
In addition to using a set, you can keep a list of allocated (or free)
keys, and find a new key before inserting. For a map indexed by
int, you can simply take the last element, and increment its key. But
I rather think I'd go with a simple std::vector; if deletion isn't
supported, you can do something simple like:
int key = myVector.size();
myVector.push_back( newEntry );
If you need to support deletions, then using a vector of some sort of
"maybe" type (boost::optional, etc.—you probably already have
one in your toolbox, maybe under the name of Fallible or Maybe) might be
appropriate. Depending on use patterns (number of deletions compared to
total entries, etc.), you may want to search the vector in order to
reuse entries. If your really ambitious, you could keep a bitmap of the
free entries, setting a bit each time you delete and entry, and
resetting it whenever you reuse the space.
You can add object to an std::set, and then later put the whole set into a map. But no, you can't put a value into a map without a key.
The closest thing to what you're trying to do is probably
myMap[myMap.size()] = "some string";
The only advantage this has over std::set is that you can pass the integer indexes around to other modules without them needing to know the type of std::set<Foo>::iterator or similar.
It is impossible. Such an operation would require intricate knowledge of the key type to know which keys are available. For example, std::map would have to increment int values for int maps or append to strings for string maps.
You could use a std::set and drop keying altogether.
If you want to achieve something similar to automatically generated primary keys in SQL databases than you can maintain a counter and use it to generate a unique key. But perhaps std::set is what you really need.

Container for database-like searches

I'm looking for some STL, boost, or similar container to use the same way indexes are used in databases to search for record using a query like this:
select * from table1 where field1 starting with 'X';
or
select * from table1 where field1 like 'X%';
I thought about using std::map, but I cannot because I need to search for fields that "start with" some text, and not those that are "equal to". Beside that, I need it to work on multiple fields (each "record" has 6 fields, for example), so I would need a separate std::map for each one.
I could create a sorted vector or list and use binary search (breaking the set in 2 in each step by reading the element in the middle and seeing if it's more or less than 'X'), but I wonder if there is some ready-made container I could use without reinventing the wheel?
Boost.Multi-Index allows you to manage with several index and it implements the lower_bound as for std::set/map. You will need to select the index corresponding to the field and then do as if it was a map or a set.
Next follows a generic function that could be used to get a couple of iterators, the fist to the first item starting with a given prefix, the second the first item starting with the next prefix, i.e. the end of the search
template <typename SortedAssociateveContainer>
std::pair<typename SortedAssociateveContainer::iterator,
typename SortedAssociateveContainer::iterator>
starts_with(
SortedAssociateveContainer const& coll,
typename SortedAssociateveContainer::key_type const& k)
{
return make_pair(coll.lower_bound(k),
coll.lower_bound(next_prefix(k));
}
where
next_prefix gets the next prefix using lexicographic order based on the SortedAssociateveContainer comparator (of course this function needs more arguments to be completely generic, see the question).
The result of starts_with can be used on any range algorithm (see Boost.Range)
std::map is fine, or std::set if there's no data other than the string. Pass your prefix string into lower_bound to get the first string which sorts at or after that point. Then iterate forward through the map until you hit the end or find an element which doesn't begin with your prefix.