c++ Container that preserve insertion order - c++

I have a std::map which store a key with a vector of std::any.
The purpose is to store all values and print them (with the different types) for each key.
No other operations are performed on the container, only "insertion" and "clean".
I want to clarify that the container is filled (and emptied) very frequently, so i need an efficient container.
It all works with my code. The problem, however, is that when i print the values they are sorted according to the key, but i have to print (or store) them by insertion order.
My code:
#include <iostream>
#include <map>
#include <vector>
#include <any>
std::map<int, std::vector<std::any>> testMap;
void insertMap(int value, std::vector<std::any> tempVector);
void printMap();
int main()
{
std::vector<std::any> tempVector;
tempVector.clear();
tempVector.push_back(1000);
tempVector.push_back((std::string)"hello");
tempVector.push_back(0.10f);
insertMap(10, tempVector);
tempVector.clear();
tempVector.push_back(1500);
tempVector.push_back((std::string)"hello2");
tempVector.push_back(0.20f);
insertMap(5, tempVector);
tempVector.clear();
tempVector.push_back(2000);
tempVector.push_back((std::string)"hello3");
tempVector.push_back(0.5f);
insertMap(7, tempVector);
// etc..
printMap();
}
void insertMap(int value, std::vector<std::any> tempVector)
{
testMap[value].insert(testMap[value].end(), tempVector.begin(), tempVector.end());
}
void printMap()
{
for (const auto& [key, value] : testMap)
{
std::cout << "key=" << key << "\n";
for(auto vec_iter : value)
{
if (vec_iter.type() == typeid(int))
std::cout << "\t" << "int=" << std::any_cast<int>(vec_iter) << "\n";
else if (vec_iter.type() == typeid(float))
std::cout << "\t" << "float=" << std::any_cast<float>(vec_iter) << "\n";
else if (vec_iter.type() == typeid(std::string))
std::cout << "\t" << "string=" << std::any_cast<std::string>(vec_iter) << "\n";
}
}
}
Output:
key=5
key=7
key=10
Expected output:
key=10
key=5
key=7
I tried to using unordered_map but it doesn't print them by insertion order.
So which container could i use? What could be the best performance in my case?
I thought that i could use a std::vector< std::map<int, std::vector<std::any>> > (vector that store std::map). But is it fast? Is there a better solution?

There is no standard library container that both provides fast access by key (which is presumably why you're using std::map to begin with) and "preserves insertion order". If you really need access by key, then iteration order is something you give up control over.
If you need to recover the order of insertion, then you are going to have to preserve it. The simplest way to do that is to just store a vector of map iterators alongside your map. When you insert an item into the map, push the new iterator for it to the back of the vector too.

If you are in a position to use Boost, Boost.MultiIndex can be resorted to:
Live Coliru Demo
#include <iostream>
#include <vector>
#include <any>
#include <utility>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/key.hpp>
struct test_map_value_type
{
test_map_value_type(int first, const std::vector<std::any>& second):
first{first},second{second}{}
int first;
mutable std::vector<std::any> second;
};
boost::multi_index_container<
test_map_value_type,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<
boost::multi_index::key<&test_map_value_type::first>
>,
boost::multi_index::sequenced<>
>
> testMap;
void insertMap(int value, std::vector<std::any> tempVector);
void printMap();
int main()
{
std::vector<std::any> tempVector;
tempVector.clear();
tempVector.push_back(1000);
tempVector.push_back((std::string)"hello");
tempVector.push_back(0.10f);
insertMap(10, tempVector);
tempVector.clear();
tempVector.push_back(1500);
tempVector.push_back((std::string)"hello2");
tempVector.push_back(0.20f);
insertMap(5, tempVector);
tempVector.clear();
tempVector.push_back(2000);
tempVector.push_back((std::string)"hello3");
tempVector.push_back(0.5f);
insertMap(7, tempVector);
// etc..
printMap();
}
void insertMap(int value, std::vector<std::any> tempVector)
{
auto it=testMap.emplace(value,std::vector<std::any>{}).first;
it->second.insert(it->second.end(), tempVector.begin(), tempVector.end());
}
void printMap()
{
for (const auto& [key, value] : testMap.get<1>())
{
std::cout << "key=" << key << "\n";
for(auto vec_iter : value)
{
if (vec_iter.type() == typeid(int))
std::cout << "\t" << "int=" << std::any_cast<int>(vec_iter) << "\n";
else if (vec_iter.type() == typeid(float))
std::cout << "\t" << "float=" << std::any_cast<float>(vec_iter) << "\n";
else if (vec_iter.type() == typeid(std::string))
std::cout << "\t" << "string=" << std::any_cast<std::string>(vec_iter) << "\n";
}
}
}
Output
key=10
int=1000
string=hello
float=0.1
key=5
int=1500
string=hello2
float=0.2
key=7
int=2000
string=hello3
float=0.5

Related

How to insert vector of integers into Key, Value of std::map

Goal: Read numerical text files into vectors and then add the vectors to key,value std::map so that I can reference them by the key name I have specified for them, later.
Thought this would be easy and I am surprised that I can't find an answer for this already on StackOverflow.
Result Expected:
Print1 = {100,200,500,600}
Print2 = {7890,5678,34567,3,56}
Print3["NameA"] = Print1
Print3["NameB"] = Print2
If my process is inefficient or going in the wrong direction, I would appreciate the pointers.
I keep getting Semantic Issue build fails and no viable conversion from pair <const basic_string>
Current Code:
#include <string.h>
#include <iostream>
#include <map>
#include <utility>
#include <vector>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
int main()
{
std::vector<int> print1;
std::ifstream inputFile("numbers.txt");
// test file open
if (inputFile)
{
double value;
// read the elements in the file into a vector
while ( inputFile >> value ) {
print1.push_back(value);
}
}
inputFile.close();
std::vector<int> print2;
std::ifstream inputFile2("numbers2.txt");
// test file open
if (inputFile2)
{
double value;
// read the elements in the file into a vector
while ( inputFile2 >> value ) {
print2.push_back(value);
}
}
inputFile2.close();
std::map<std::string, std::vector<int>> contacts;
contacts["alice"] = print1;
contacts["bob"] = print2;
std::vector<std::string> keys(contacts.size());
std::vector<int> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
std::cout << "Keys:\n";
copy(keys.begin(), keys.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
std::cout << "\n";
std::cout << "Values:\n";
copy(values.begin(), values.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
You can reference map element directly which will create an entry if doesn't exist, and just fill it from the file read loop:
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <string>
int main()
{
std::map<std::string, std::vector<int>> m;
int num;
auto &&alice = m["alice"];
std::ifstream if_alice("numbers1.txt");
while (if_alice >> num)
alice.push_back(num);
if_alice.close();
auto &&bob = m["bob"];
std::ifstream if_bob("numbers2.txt");
while (if_bob >> num)
bob.push_back(num);
if_bob.close();
// test
for (auto &&data : m)
{
std::cout << "Name: " << data.first << "\t";
for (int num : data.second)
std::cout << num << " ";
std::cout << "\n";
}
}
First of all, there is no point in arguing that Xcode didn't show any error msgs for your code. Try either turning on all the compiler warnings or try in online compilers. The result will be not disappointing: https://godbolt.org/z/cU54GX
If I have understood correctly, you want to store your information from two files(the integer values) in a std::map, where its key = std::string and value = vector of integer array.
If so,
1. You have your problem starting from reading integers from files.
There you are using double for no reason and storing to
std::vector<int> (i,e print1 and print2).
2. Secondly, what if your file has not been open?. inputFile.close();
and inputFile2.close(); will close it anyway, without knowing the
fact. This is wrong.
The proper way would be:
inputFile.open("numbers.txt", std::ios::in); // opening mode
if (inputFile.is_open()) {
// do stuff
inputFile.close(); // you need closing only when file has been opened
}
3. If your intention is to only print keys and values, you don't
need to parse them to different vectors.
You can do it directly:
for(const std::pair<kType, vType>& mapEntry: contacts)
{
std::cout << "Key: " << mapEntry.first << " Values: ";
for(const int values: mapEntry.second) std::cout << values << " ";
std::cout << std::endl;
}
In c++17 you can use Structured binding
for(const auto& [Key, Values]: contacts)
{
std::cout << "Key: " << Key << " Values: ";
for(const int value: Values) std::cout << value << " ";
std::cout << std::endl;
}
4. If you really want to parse them to a different vector; first of all, the data structure for storing keys is wrong:
std::vector<int> values(contacts.size());
^^^^^^
which should have been a vector of vector of integers, as your vType = std::vector<int>. That is,
std::vector<std::vector<int>> values
^^^^^^^^^^^^^^^^^^
Secondly, you have the functions key(const std::pair<std::string, std::string>& keyValue) and value(const std::pair<std::string, std::string>& keyValue) wrong, where you are passing a pair of strings as keys and values.
This should have been
typedef std::string kType; // type of your map's key
typedef std::vector<int> vType;// value of your map's value
std::pair<kType, vType>
However, you can simply replace with lambdas, which would be more intuitive, in the sense of having the functions next to the line where you needed. For example,
std::vector<kType> keysVec;
keysVec.reserve(contacts.size());
auto getOnlyKeys = [](const std::pair<kType, vType>& mapEntry){ return mapEntry.first; };
std::transform(contacts.begin(), contacts.end(), std::back_inserter(keysVec), getOnlyKeys);
See an example code here

I want to reverse the values of map and print it using range based for loop.

I have done the programming but it is not reversing. I have used a different map to put the values in reverse order,but it still shows the same. My main question was to traverse backward and print the values using range based loop.
#include "stdafx.h"
#include <iostream>
#include<conio.h>
#include <stdio.h>
#include<vector>
#include<map>
#include<utility>
#include<set>
map<int, int>m1;
for (int i = 1; i <= 100; ++i)
{
m1.insert({ i,i });
}
for (const auto &y :m1)
{
cout <<"("<< y.first << " "<<y.second << ")" <<" " ;
}
cout << endl << endl;
map<int, int>m2;
map<int, int>::reverse_iterator iter;
for (auto iter = m1.rbegin(); iter != m1.rend(); ++iter)
{
m2.insert({ iter->first,iter->second });
}
for (const auto &y : m2)
{
cout << "(" << y.first << " " << y.second << ")" << " ";
}
As Some Programmer Dude pointed out, but for the completeness of my answer, a std::map is sorted on the key, no matter what order you insert the elements. One option would be to create a new map with the opposite sorting, but that doesn't seem to be what you really want.
It seems you know how about reverse iterators, but not how to get at them when using range-based for. Since it operates on a range, i.e. some type that provides begin and end iterators, you need to create some wrapper around your map that provides this.
Here's a general one I just put together than works in C++11. It won't cover every possible case, and can be made a bit neater in C++14, but it will work for you.
#include <iostream>
#include <iterator>
// The wrapper type that does reversal
template <typename Range>
class Reverser {
Range& r_;
public:
using iterator_type = std::reverse_iterator<decltype(std::begin(r_))>;
Reverser(Range& r) : r_(r) {}
iterator_type begin() { return iterator_type(std::end(r_)); }
iterator_type end() { return iterator_type(std::begin(r_)); }
};
// Helper creation function
template <typename Range>
Reverser<Range> reverse(Range& r)
{
return Reverser<Range>(r);
}
int main()
{
int vals[] = {1, 2, 3, 4, 5};
for (auto i : reverse(vals))
std::cout << i << '\n';
}
This outputs:
$ ./reverse
5
4
3
2
1
(You may also find libraries that provide a similar adapter; Eric Niebler is working on a ranges library for The Standard.)
Also, please reconsider your use of what are often considered bad practices: using namespace std; and endl (those are links to explanations).
Here's an example of iterating backward through a std::map:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<int, int> m;
m[1] = 1;
m[2] = 2;
m[3] = 3;
for (auto iter = m.rbegin(); iter != m.rend(); ++iter) {
std::cout << iter->first << ": " << iter->second << std::endl;
}
}
If you are pre-C++11, you'll just need to spell out auto, which is:
std::map<int, int>::reverse_iterator
If you're using boost, you can use a range-based for loop with a reverse adapter:
#include <boost/range/adaptor/reversed.hpp>
for (auto& iter : boost::adaptors::reverse(m)) {
std::cout << iter.first << ": " << iter.second << std::endl;
}
If you only need to print the elements in the map in reverse order,you don't need another map for it,you can do this:
std::map<int, int>::reverse_iterator iter;
for (iter = m1.rbegin(); iter != m1.rend(); ++iter)
{
std::cout << "(" << iter->first << " " << iter->second << ")" << " ";
}

Quicker way to insert into list?

I'm having a hard time writing an efficient way to insert a large volume of values into a list, without using maps. The code I have currently looks as follows:
list<pair<int, vector<int>>> myList;
void key_value_sequences::insert(int key, int value){
for(it=myList.begin(); it!=myList.end(); ++it){
if(it->first==key){
it->second.push_back(value);
return;
}
}
vector<int> v;
v.push_back(value);
myList.push_back(make_pair(key, v));
myList.sort();
return;
};
Any help is greatly appreciated.
You're close; since you appear to have an in-order list, try this:
void key_value_sequences::insert(int key, int value){
for(it=myList.begin(); it!=myList.end(); ++it){
if(it->first==key){
it->second.push_back(value);
return;
}
else if (it->first > key) // Or <, depending on order
{
vector<int> v;
v.push_back(value);
myList.insert(it, make_pair(key, v));
return;
}
}
vector<int> v;
v.push_back(value);
myList.push_back(make_pair(key, v));
}
Basically, once you go one past the key, insert the new entry, or if you get to the end, you've got the largest key, so push it to the back. Thus you can keep the list sorted.
A simple hash table using a vector and a list.
The vector contains a list of collided keys. A smart hash table will adjust it's size and rehash to minimize collisions. This is not a smart hash table.
The vector is used as the outer container because it has much faster iteration. There is a string case for the inner container also being a vector because lists... they kinda suck unless you add and remove more than you iterate. I'll let the big man himself explain.
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <stdexcept>
class poor_mans_hash
{
std::vector<std::list<std::pair<int,int>>> hash_table;
int hash(int val)
{
return abs(val%static_cast<int>(hash_table.size()));
// abs because a negative number makes a very explosive vector index
}
public:
poor_mans_hash(int size): hash_table(size)
{
// all work done in member initialization list
}
void insert(int key,
int val)
{
// get list for correct element
std::list<std::pair<int,int>> &collisionlist = hash_table[hash(key)];
// see if key is in list
for (std::pair<int,int> &test: collisionlist)
{
if (key == test.first)
{
test.second = val; // update existing
return;
}
}
// key not found. Add it.
collisionlist.push_back(std::pair<int,int>(key, val));
}
bool find(int key)
{
for (std::pair<int,int> &test: hash_table[hash(key)])
{
if (key == test.first)
{
return true;
}
}
return false;
}
int & get(int key)
{
for (std::pair<int,int> &test: hash_table[hash(key)])
{
if (key == test.first)
{
// found key. Return value
return test.second;
}
}
// Not found.
throw std::out_of_range("No such key");
}
};
int main()
{
poor_mans_hash test(50);
test.insert(1, 1);
std::cout << "1: " << test.get(1) << std::endl;
try
{
std::cout << test.get(51) << std::endl;
}
catch (std::out_of_range &e)
{
std::cout << "could not get 51: " << e.what() << std::endl;
}
test.insert(11, 11);
test.insert(21, 21);
test.insert(51, 51);
std::cout << "1: " << test.get(1) << std::endl;
std::cout << "51: " << test.get(51) << std::endl;
test.insert(1, 2);
std::cout << "1: " << test.get(1) << std::endl;
}
Possible improvements to this are many. Rehashing and smarter organizing, for one thing. For another, here's a write up on all the fun things you can do to make this a good, library-like container Writing your own STL Container

Passing map with custom comparator to function

I have an STL map with a custom comparator which I want to pass to a function, but the function doesn't recognize the custom comparator.
Trying to access the map within the main function works.
I have listed both attempts in my code.
#include <iostream>
#include <string>
#include <map>
// Error: cmpByStringLength is not recognized (both times)
void funcOut(std::map<std::string, int, cmpByStringLength> myMap)
{
for (std::map<std::string, int, cmpByStringLength>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " => " << it->second << std::endl;
}
}
int main()
{
// Reverse sort by length
struct cmpByStringLength {
bool operator()(const std::string& a, const std::string& b) const {
return a.length() > b.length();
}
};
std::map<std::string, int, cmpByStringLength> myMap;
myMap.emplace("String 1", 5);
myMap.emplace("String 123", 10);
funcOut(myMap);
// Working
for (std::map<std::string, int, cmpByStringLength>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " => " << it->second << std::endl;
}
return 0;
}
You can only use a name after its declaration, and only if it's in scope. Your comparator type is scoped within main, so you can only use it in that function. Move the definition out of main, into the global namespace (or in another namespace if you like), to make it available in other functions.
Alternatively, you could make the other function a template, so it can work with any map type:
template <typename Map>
void funcOut(Map const & myMap) {
// your code here
}
Use a template, because I'm a lazy c++ developer (I don't need to worry about lots of details...) I would do..
template <typename MapType>
void funcOut(MapType& myMap)
{
for (auto& p : myMap)
{
std::cout << p.first << " => " << p.second << std::endl;
}
}

Create links between elements in two vectors

I am planning to make links for identifying the association of two vectors.
Let's assume that we have two vectors:
vector1 <seg1, seg2, seg3>
vector2 <pic1, pic2, pic3,pic4>
And the association is assumed like:
seg1->(pic1, pic2) seg2->(pic1,pic2) seg3->pic3 //from vector1 side
pic1->(seg1, seg2) pic2->(seg1,seg2) pic3->seg3 pic4->nothing //from vector2 side
what I want is know which seg is associated with which indexNums of pics, and the same for pics. I only focus on the position number in the two vector, and I don't care what the element content of these two vectors is. What I did is to design a struct like:
Struct
{
int indexNum;
Bool type; //seg or pic
addlink(indexNum); //add a link to another Struct, and the type should be opposite.
removelink(indexNum); //remove a link from a given indexNum
getLinks(); //return all of links, the return should be vector<Struct*>?
}
I think it is not good, and not clear for the association. Is there better way to make links for this two vectors?
Boost.Bimap was designed to solve this kind problem.
I wonder if multimap will be a good solution for you
http://www.cplusplus.com/reference/map/multimap/
#include <iostream>
#include <boost/bimap.hpp>
#include <boost/bimap/set_of.hpp>
#include <boost/bimap/multiset_of.hpp>
namespace bimaps = boost::bimaps;
int main()
{
typedef boost::bimap<bimaps::multiset_of<int>, bimaps::set_of<int>> bimap_t;
typedef bimap_t::value_type value_type;
bimap_t bimap;
bimap.insert(value_type(1, 1));
bimap.insert(value_type(1, 2));
auto& left = bimap.left;
auto it = left.find(1);
std::cout << "LEFT" << std::endl;
for (; it != left.end(); ++it)
{
std::cout << it->first << " " << it->second << std::endl;
}
auto& right = bimap.right;
auto r_it = right.find(2);
std::cout << "RIGHT" << std::endl;
for (; r_it != right.end(); ++r_it)
{
std::cout << r_it->first << " " << r_it->second << std::endl;
}
}
http://liveworkspace.org/code/e766b134d9e96b9192424ac9325ae59c