I'm working on a word Tagging system for a C++ project. I need a system where a map stores the following key-value information:
word["with"] = 16, 6, 15;
Where ["with"] is the index, and the 3-tuple (16, 6, 15) are values of the index. I've tried maps, but I keep getting semantic errors, which I understand are a result of not being able to give a key more then 1 value.
I tried multi maps, but I can't seem to get the syntax to suit my needs?
I would like to refrain from using Structs or Classes, as this database already contains 200 words, and I'm trying to keep my lines of code readable and too a minimum.
How would I go about this? Am I missing something? How would you declare a system like this?
You should declare your map as std::map<std::string, std::vector<unsigned int>>, so you can have a vector of values for your index.
You can make a map that maps Strings to Vectors or some other data structure that can hold an arbitrary number of integers.
Worth noting, however, that things like Structs and Classes are components of a language meant to organize code. Structs group related data; classes model groups of related data and their associated behaviors. It's certainly possible to do everything without them but that would make for some very unreadable code.
The number of lines and whether or not you use classes/structs are poor metrics for the complexity and readability of your code. And the modularity they offer far exceeds the minute runtime cost of dereferencing those values.
word["with"] = 16, 6, 15;//This usage is wrong
std::multimap or std::unordered_multimap should work for you.
If you define word as follows:
std::multimap<std::string,int> word;
You should insert values to map as shown below:
std::string key="with";
word.insert(std::pair<std::string,int>(key,16));
word.insert(std::pair<std::string,int>(key,6));
word.insert(std::pair<std::string,int>(key,15));
for( auto &x : word)
std::cout<<x.first<<" " << x.second<<"\n";
As user4581301 pointed out in comment if you have C++11 enabled compiler, you can insert values into std::multimap as follows:
word.emplace("with",16);
word.emplace("with",6);
word.emplace("with",15);
Demo: http://coliru.stacked-crooked.com/a/c7ede5c497172c5d
Example for using C++ maps to hold multiple integer values:
#include<iostream>
#include<map>
#include<vector>
using namespace std;
int main(){
std::map<int, std::vector<int> > mymap2;
std::vector<int> myvector;
myvector.push_back(8);
myvector.push_back(11);
myvector.push_back(53);
mymap2[5] = myvector;
cout << mymap2[5][0] << endl;
cout << mymap2[5][1] << endl;
cout << mymap2[5][2] << endl;
}
Prints:
8
11
53
Just replace the int datatypes with a string and you should be able to map strings to lists of numbers.
Related
How do you make a variable name where you create a variable and then in brackets the variable number? (By the way, I'm just guessing out how the code should be so that you get what I'm trying to say.) For example:
int var[5];
//create a variable var[5], but not var[4], var[3], var[2], etc.
Then, the variable number must be able to be accessed by a variable value:
int number = 5;
int var[number]; //creates a var[5], not a var[4], etc.
int var[2]; //creates a var[2], not a var[1], etc.
cout >>var[number];
number = 2;
cin << var[number];
If I'm way off track with my "example", please suggest something else. I need something similar to this for my game to operate, because I must be able to create an unlimited instance of bullets, but they will also be destroyed at one point.
It looks like you are looking for the functionality provided by std::map which is a container used to map keys to values.
Documentation of std::map
Example use
In the below example we bind the value 123 to the integer key 4, and the value 321 to key 8. We then use a std::map<int,int>::const_iterator to iterate over the key/value pairs in our std::map named m.
#include <map>
...
std::map<int, int> m;
m[4] = 123;
m[8] = 321;
for (std::map<int, int>::const_iterator cit = m.begin (); cit != m.end (); ++cit)
std::cout << cit->first << " -> " << cit->second << std::endl;
output:
4 -> 123
8 -> 321
It looks like you want variable length arrays, which is not something C++ supports. In most cases, the correct solution is to use an std::vector instead, as in
int number = 42; // or whatever
std::vector<int> var(number);
You can use std::vector as you would use an array in most cases, and you gain a lot of bonus functionality.
If I understand what you want correctly (which I'm not certain that I do), you want to be able to create a place to hold objects and use them according to some index number, but to only create the specific objects which go in it on demand. You want do to this either because 1) you don't know how many objects you're going to create or 2) you aren't going to use every index number or 3) both.
If (1) then you should probably just use a vector, which is an array-like structure which grows automatically as you add more things to it. Look up std::vector.
If (2) then you could use an array of pointers and initially set all of the values to null and then use new to create the objects as needed. (Or you could use the solution recommend in part 3.)
If (3) then you want to use some form of map or hash table. These structures will let you find things by number even when not all numbers are in use and will grow as needed. I would highly recommend a hash table, but in C++, there isn't one in the STL, so you have to build your own or find one in a third-party library. For ease, you can use std::map, which is part of the STL. It does basically the same thing, but is slower. Some C++ distributions also include std::hash_map. If it's available, that should be used instead because it will be faster than std::map.
There are two ways in which I can easily make a key,value attribution in C++ STL: maps and sets of pairs. For instance, I might have
map<key_class,value_class>
or
set<pair<key_class,value_class> >
In terms of algorithm complexity and coding style, what are the differences between these usages?
They are semantically different. Consider:
#include <set>
#include <map>
#include <utility>
#include <iostream>
using namespace std;
int main() {
pair<int, int> p1(1, 1);
pair<int, int> p2(1, 2);
set< pair<int, int> > s;
s.insert(p1);
s.insert(p2);
map<int, int> m;
m.insert(p1);
m.insert(p2);
cout << "Set size = " << s.size() << endl;
cout << "Map size = " << m.size() << endl;
}
http://ideone.com/cZ8Vjr
Output:
Set size = 2
Map size = 1
Set elements cannot be modified while they are in the set. set's iterator and const_iterator are equivalent. Therefore, with set<pair<key_class,value_class> >, you cannot modify the value_class in-place. You must remove the old value from the set and add the new value. However, if value_class is a pointer, this doesn't prevent you from modifying the object it points to.
With map<key_class,value_class>, you can modify the value_class in-place, assuming you have a non-const reference to the map.
map<key_class,value_class> will sort on key_class and allow no duplicates of key_class.
set<pair<key_class,value_class> > will sort on key_class and then value_class if the key_class instances are equal, and will allow multiple values for key_class
The basic difference is that for the set the key is the pair, whereas for the map the key is key_class - this makes looking things up by key_class, which is what you want to do with maps, difficult for the set.
Both are typically implemented with the same data structure (normally a red-black balanced binary tree), so the complexity for the two should be the same.
std::map acts as an associative data structure. In other words, it allows you to query and modify values using its associated key.
A std::set<pair<K,V> > can be made to work like that, but you have to write extra code for the query using a key and more code to modify the value (i.e. remove the old pair and insert another with the same key and a different value). You also have to make sure there are no more than two values with the same key (you guessed it, more code).
In other words, you can try to shoe-horn a std::set to work like a std::map, but there is no reason to.
To understand algorithmic complexity, you first need to understand the implementation.
std::map is implemented using RB-tree where as hash_map are implemented using arrays of linked list. std::map provides O(log(n)) for insert/delete/search operation, hash_map is O(1) is best case and o(n) in worst case depending upon hash collisions.
Visualising that semantic difference Philipp mentioned after stepping through the code, note how map key is a const int and how p2 was not inserted into m:
I have a std::map that I'm using to store values for x and y coordinates. My data is very sparse, so I don't want to use arrays or vectors, which would result in a massive waste of memory. My data ranges from -250000 to 250000, but I'll only have a few thousand points at the most.
Currently I'm creating a std::string with the two coordinates (i.e. "12x45") and using it as a key. This doesn't seem like the best way to do it.
My other thoughts were to use an int64 and shove the two int32s into it and use it as a key.
Or to use a class with the two coordinates. What are the requirements on a class that is to be used as the key?
What is the best way to do this? I'd rather not use a map of maps.
Use std::pair<int32,int32> for the key:
std::map<std::pair<int,int>, int> myMap;
myMap[std::make_pair(10,20)] = 25;
std::cout << myMap[std::make_pair(10,20)] << std::endl;
I usually solve this kind of problem like this:
struct Point {
int x;
int y;
};
inline bool operator<(const Point& p1, const Point& p2) {
if (p1.x != p2.x) {
return p1.x < p2.x;
} else {
return p1.y < p2.y;
}
}
Boost has a map container that uses one or more indices.
Multi Index Map
What are the requirements on a class that is to be used as the key?
The map needs to be able to tell whether one key's value is less than another key's value: by default this means that (key1 < key2) must be a valid boolean expression, i.e. that the key type should implement the 'less than' operator.
The map template also implements an overloaded constructor which lets you pass-in a reference to a function object of type key_compare, which can implement the comparison operator: so that alternatively the comparison can be implemented as a method of this external function object, instead of needing to be baked in to whatever type your key is of.
This will stuff multiple integer keys into a large integer, in this case, an _int64. It compares as an _int64, AKA long long (The ugliest type declaration ever. short short short short, would only be slightly less elegant. 10 years ago it was called vlong. Much better. So much for "progress"), so no comparison function is needed.
#define ULNG unsigned long
#define BYTE unsigned char
#define LLNG long long
#define ULLNG unsigned long long
// --------------------------------------------------------------------------
ULLNG PackGUID(ULNG SN, ULNG PID, BYTE NodeId) {
ULLNG CompKey=0;
PID = (PID << 8) + NodeId;
CompKey = ((ULLNG)CallSN << 32) + PID;
return CompKey;
}
Having provided this answer, I doubt this is going to work for you, as you need two separate and distinct keys to navigate with in 2 dimensions, X and Y.
On the other hand, if you already have the XY coordinate, and just want to associate a value with that key, then this works spectacularly, because an _int64 compare takes the same time as any other integer compare on Intel X86 chips - 1 clock.
In this case, the compare is 3X as fast on this synthetic key, vs a triple compound key.
If using this to create a sparsely populated spreadsheet, I would RX using 2 distinct trees, one nested inside the other. Make the Y dimension "the boss", and search Y space first to resolution before proceeding to the X dimension. Spreadsheets are taller than they are wide, and you always want the 1st dimension in any compound key to have the largest number of unique values.
This arrangement would create a map for the Y dimension that would have a map for the X dimension as it's data. When you get to a leaf in the Y dimension, you start searching it's X dimension for the column in the spreadsheet.
If you want to create a very powerful spreadsheet system, add a Z dimension in the same way, and use that for, as an example, organizational units. This is the basis for a very powerful budgeting/forecasting/accounting system, one which allows admin units to have lots of gory detail accounts to track admin expenses and such, and not have those accounts take up space for line units which have their own kinds of detail to track.
I think for your use case, std::pair, as suggested in David Norman's answer, is the best solution. However, since C++11 you can also use std::tuple. Tuples are useful if you have more than two keys, for example if you have 3D coordinates (i.e. x, y, and z). Then you don't have to nest pairs or define a comparator for a struct. But for your specific use case, the code could be written as follows:
int main() {
using tup_t = std::tuple<int, int>;
std::map<tup_t, int> m;
m[std::make_tuple(78, 26)] = 476;
tup_t t = { 12, 45 }; m[t] = 102;
for (auto const &kv : m)
std::cout << "{ " << std::get<0>(kv.first) << ", "
<< std::get<1>(kv.first) << " } => " << kv.second << std::endl;
return 0;
}
Output:
{ 12, 45 } => 102
{ 78, 26 } => 476
Note: Since C++17 working with tuples has become easier, espcially if you want to access multiple elements simultaneously.
For example, if you use structured binding, you can print the tuple as follows:
for (auto const &[k, v] : m) {
auto [x, y] = k;
std::cout << "{ " << x << ", " << y << " } => " << v << std::endl;
}
Code on Coliru
Use std::pair. Better even use QHash<QPair<int,int>,int> if you have many of such mappings.
Hope you will find it useful:
map<int, map<int, int>> troyka = { {4, {{5,6}} } };
troyka[4][5] = 7;
An alternative for the top result that is slightly less performant but allows for easier indexing
std::map<int, std::map<int,int>> myMap;
myMap[10][20] = 25;
std::cout << myMap[10][20] << std::endl;
First and foremost, ditch the string and use 2 ints, which you may well have done by now. Kudos for figuring out that a tree is the best way to implement a sparse matrix. Usually a magnet for bad implementations it seems.
FYI, a triple compound key works too, and I assume a pair of pairs as well.
It makes for some ugly sub-scripting though, so a little macro magic will make your life easier. I left this one general purpose, but type-casting the arguments in the macro is a good idea if you create macros for specific maps. The TresKey12 is tested and running fine. QuadKeys should also work.
NOTE: As long as your key parts are basic data types you DON'T need to write anything more. AKA, no need to fret about comparison functions. The STL has you covered. Just code it up and let it rip.
using namespace std; // save some typing
#define DosKeys(x,y) std::make_pair(std::make_pair(x,y))
#define TresKeys12(x,y,z) std::make_pair(x,std::make_pair(y,z))
#define TresKeys21(x,y,z) std::make_pair(std::make_pair(x,y),z))
#define QuadKeys(w,x,y,z) std::make_pair(std::make_pair(w,x),std::make_pair(y,z))
map<pair<INT, pair<ULLNG, ULLNG>>, pIC_MESSAGE> MapMe;
MapMe[TresKey12(Part1, Part2, Part3)] = new fooObject;
If someone wants to impress me, show me how to make a compare operator for TresKeys that doesn't rely on nesting pairs so I can use a single struct with 3 members and use a comparison function.
PS: TresKey12 gave me problems with a map declared as pair,z as it makes x,pair, and those two don't play nice. Not a problem for DosKeys, or QuadKeys. If it's a hot summer Friday though, you may find an unexpected side-effect of typing in DosEquis
... err.. DosKeys a bunch of times, is a thirst for Mexican beer. Caveat Emptor. As Sheldon Cooper says, "What's life without whimsy?".
I used list to place cities into a trip. Then I iterate over
the list to display the trip itinerary. I would like to access
the cities by the name rather than by the trip order. So, I
thought I could use a map rather than a list but the key determines
the order. I would still like to control the order of the sequence
but be able to access the entries by a key.
Can these features be combined? Is there some standard way to address
this?
#include <list>
#include <iostream>
struct City{
City(std::string a_n, int a_d):name(a_n), duration(a_d){}
std::string name;
int duration;
};
int main(){
std::list<City*> trip;
trip.push_back(new City("NY", 5));
trip.push_back(new City("LA", 2));
for (std::list<City*>::iterator ii=trip.begin(); ii!=trip.end(); ++ii)
std::cout << (*ii)->name << " for " << (*ii)->duration << " days." <<std::endl;
}
Often times you will need to compose multiple lists and maps. The common way is to store a pointer to the Cities in your by city lookup map from the pointers in your list. Or you can use a class like Boost.MultiIndex to do what you want in what I would say is much cleaner. It also scales much better and there is a lot less boiler plate code if you want to add new indexes. It is also usually more space and time efficient
typedef multi_index_container<
City,
indexed_by<
sequenced<>, //gives you a list like interface
ordered_unique<City, std::string, &City::name> //gives you a lookup by name like map
>
> city_set;
Create a map<string,int> m;, where the values are indexes to a vector<City>, for example m["NY"] == 0 and m["LA"] == 1.
Use two collections:
A list to store the actual objects in the order you are interested in.
A map to map names to the objects.
The best solution is to use Boost.MultiIndex, though that's slightly more involved. Unfortunately, I don't have time now to provide sample code; sorry.
Is there any way to check if a given index of an array exists?
I am trying to set numerical index but something like 1, 5, 6,10. And so I want to see if these indexes already exist and if they do just increase another counter.
I normally work with php but I am trying to do this in c++, so basically I am trying to ask if there is an isset() way to use with c++
PS: Would this be easier with vectors? If so, can anyone point me to a good vector tutorial? Thanks
In C++, the size of an array is fixed when it is declared, and while you can access off the end of the declared array size, this is very dangerous and the source of hard-to-track-down bugs:
int i[10];
i[10] = 2; // Legal but very dangerous! Writing on memory you don't know about
It seems that you want array-like behavior, but without all elements being filled. Traditionally, this is in the realms of hash-tables. Vectors are not such a good solution here as you will have empty elements taking up space, much better is something like a map, where you can test if an element exists by searching for it and interpreting the result:
#include <map>
#include <string>
// Declare the map - integer keys, string values
std::map<int, std::string> a;
// Add an item at an arbitrary location
a[2] = std::string("A string");
// Find a key that isn't present
if(a.find(1) == a.end())
{
// This code will be run in this example
std::cout << "Not found" << std::endl;
}
else
{
std::cout << "Found" << std::endl;
}
One word of warning: Use the above method to find if a key exists, rather than something like testing for a default value
if(a[2] == 0)
{
a[2] = myValueToPutIn;
}
as the behavior of a map is to insert a default constructed object on the first access of that key value, if nothing is currently present.
My personal vote is for using a vector. They will resize dynamically, and as long as you don't do something stupid (like try and access an element that doesn't exist) they are quite friendly to use.
As for tutorials the best thing I could point you towards is a google search
To do this without vectors, you can simply cross-check the index you are tying to access with the size of array. Like: if(index < array_size) it is invalid index.
In case the size is not known to you, you can find it using the sizeof operator.
For example:
int arr[] = {5, 6, 7, 8, 9, 10, 1, 2, 3};
int arr_size = sizeof(arr)/sizeof(arr[0]);
It sounds to me as though really a map is closest to what you want. You can use the Map class in the STL (standard template library)(http://www.cppreference.com/wiki/stl/map/start).
Maps provide a container for objects which can be referenced by a key (your "index").