Quicker way to insert into list? - c++

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

Related

How to transfer object to function as it's class'es member

For example: I have class "ListNode" which is linked list member, and i overloaded a [] operator for List class itself, but every time i need to get data from a node, i need to write list[0]->getData() or if I want to set new data, I need to write list[0]->setData(*somedata*).
But I want to make it be able to work like for example like list[0] = 5, so first Node's data is setted to 6. Same with functions. What should I do if I have Node which has int data, and I need to transfer it's data to a function, but without writing Node->getData() all the time?
You can make your operator[] return a reference to the element you want to access.
The example below is a simplified version of what would be a proper solution, including a const version for the operator[].
[Demo]
#include <iostream> // cout
#include <stdexcept> // range_error
struct Node
{
int value{};
Node* next{};
int& operator[](size_t pos)
{
if (pos == 0) { return this->value; }
else if (next == nullptr) { throw std::range_error{"out of bounds"}; }
else { return (*next)[pos - 1]; }
}
};
int main()
{
Node n5{5, nullptr};
Node n4{4, &n5};
Node n3{3, &n4};
Node n2{2, &n3};
Node list{1, &n2};
try
{
std::cout << "list[3]: " << list[3] << "\n";
list[3] = 25;
std::cout << "list[3]: " << list[3] << "\n";
std::cout << "list[13]: " << list[13] << "\n";
}
catch (const std::exception& exc)
{
std::cout << "Error: " << exc.what() << ".\n";
}
}

c++ Container that preserve insertion order

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

Recursively adding subtrees to boost property tree

I want to write a parameter server in C++ where I can recursively dump a tree of parameters into a property tree and then write it to a JSON file.
The dump function looks like this:
void Params::dump(string filename) {
// Create a root
pt::ptree root;
// Fill the root with parameters
mapToPt(curParams, root);
// Write to cout
pt::write_json(cout, root);
}
mapToPt is supposed to recursively go through the hierarchy of my parameter server and fill the property tree while doing so:
void Params::mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root) {
// Fill current root with parameters from curParams ParameterMap
map<string, boost::shared_ptr<Param> >::iterator it;
for ( it = curParams->getParamMap().begin(); it != curParams-getParamMap().end(); it++ ) {
root.put(it->first, it->second->getValue());
cout << "Add Parameter: \n";
cout << "Parameter name: " << it->first << "\n";
cout << "Parameter value: " << it->second->getValue() << "\n";
}
// Recursively go through all children to do the same to them
if(curParams->hasChildren()) { //ERROR LINE
map<string, boost::shared_ptr<Params> >::iterator it;
for ( it = curParams->getChildren().begin(); it != curParams-getChildren().end(); it++ ) {
pt::ptree new_tree;
root.add_child(it->second->getName(), new_tree);
cout << "Add Child: \n";
cout << "Child name: " << it->second->getName() << "\n";
mapToPt(it->second, new_tree);
}
}
}
My problem is that as soon as I go into recursion, errors occur at random lines that can not be the cause of the error. "basic_string::_M_construct null not valid" is the error message. I believe that I might access deleted content and that it might be due to the way I iterate through the property tree children.
Is my way of doing that wrong or is there another way to do it?
Thank you.
Why is mapToPt a member when it also expects a pointer to a Params instance?
Anyhoops, there's quite a bit of confusion.
At a design level, your Params type looks like it cannot decide whether it's a leaf node or not. Moreover, it suffers from "Quasi Classes" design, where getters essentially guarantee that there is no class invariant possible. In such cases, prefer to just have a struct with member fields.
Note, if you fail to return by reference from getParamMap() and getChildren() then you already have Undefined Behaviour in both loops, because the iterators then point into non-existent copies of containers.
You should check this. Also, see my working demo below
At the implementation level, this is causing you problems:
pt::ptree new_tree;
root.add_child(it->second->getName(), new_tree);
add_child inserts a copy of new_tree. Any future modification to new_tree has no effect. Instead, write:
pt::ptree& new_tree = root.add_child(it->second->getName(), {});
Here, new_tree becomes a reference to the actually added tree.
Attempted Fix
The style is still below my expectations. Personally I'd closely review the use of shared_ptr at all in this piece of code.
But this will probably help you along:
Live On Coliru
#include <boost/make_shared.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
#include <map>
namespace pt = boost::property_tree;
struct Param {
std::string getValue() const { return "42"; }
};
struct Params {
using ParamMap = std::map<std::string, boost::shared_ptr<Param> >;
using Children = std::map<std::string, boost::shared_ptr<Params> >;
Params(std::string name = "") : _name(name) {}
std::string getName() const { return _name; }
ParamMap& getParamMap() { return _map; }
ParamMap const& getParamMap() const { return _map; }
bool hasChildren() const { return !_children.empty(); }
Children& getChildren() { return _children; }
Children const& getChildren() const { return _children; }
static void mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root);
private:
std::string _name;
ParamMap _map;
Children _children;
};
void Params::mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root) {
// Fill current root with parameters from curParams ParameterMap
std::map<std::string, boost::shared_ptr<Param> >::iterator it;
for (it = curParams->getParamMap().begin(); it != curParams->getParamMap().end(); it++) {
root.put(it->first, it->second->getValue());
//std::cout << "Add Parameter: \n";
//std::cout << "Parameter name: " << it->first << "\n";
//std::cout << "Parameter value: " << it->second->getValue() << "\n";
}
// Recursively go through all children to do the same to them
if (curParams->hasChildren()) {
for (auto it = curParams->getChildren().begin(); it != curParams->getChildren().end(); it++) {
pt::ptree& new_tree = root.add_child(it->second->getName(), {});
//std::cout << "Add Child: \n";
//std::cout << "Child name: " << it->second->getName() << "\n";
mapToPt(it->second, new_tree);
}
}
}
int main() {
auto a = boost::make_shared<Params>("rootparams");
a->getParamMap().emplace("one", boost::make_shared<Param>());
a->getParamMap().emplace("two", boost::make_shared<Param>());
a->getParamMap().emplace("three", boost::make_shared<Param>());
a->getChildren().emplace("child1", boost::make_shared<Params>("child1-name"))
.first->second->getParamMap().emplace("four", boost::make_shared<Param>());
a->getChildren().emplace("child2", boost::make_shared<Params>("child2-name"))
.first->second->getParamMap().emplace("five", boost::make_shared<Param>());
pt::ptree root;
a->mapToPt(a, root);
write_json(std::cout, root);
}
Prints
{
"one": "42",
"three": "42",
"two": "42",
"child1-name": {
"four": "42"
},
"child2-name": {
"five": "42"
}
}

map inserting keys but not value

I am playing with c++ code today. Learning about std containers. I'm trying to insert and update data in a std::map but for some reason I can't insert values into a map. Keys will insert but not values. The code at the bottom will print the following if you enter something into the terminal that opens. In this example I entered "test". Anyway, my questions are, why is the insert returning false, why in the value not inserting?
test
first
failed
Context1 :
Here is the code:
#include "stdafx.h"
#include <string>
#include <iostream>
#include <map>
#include <random>
static std::map<std::string, std::string> currentFullState;
static const std::string sDEFAULT_STRING = "";
void PringCurrentState()
{
std::map<std::string, std::string>::iterator stateData = currentFullState.begin();
while (stateData != currentFullState.end())
{
std::cout << stateData->first << " : ";
std::cout << stateData->second << std::endl;
stateData++;
};
}
void UpdateState(std::string context, std::string data)
{
if (currentFullState[context] == sDEFAULT_STRING)
{
// first entry, possibly special?
std::cout << "first" << std::endl;
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
if (result.second == false)
std::cout << "failed" << std::endl;
else
std::cout << "good" << std::endl;
}
else if (data != currentFullState[context])
{
// change in value
}
else
{
currentFullState[context] == data;
}
}
void DoWork()
{
if (rand() % 2)
{
UpdateState("Context1", "Data1");
}
else
{
UpdateState("Context2", "Data2");
}
}
int main()
{
std::string command = "";
for (;;)
{
PringCurrentState();
std::cin >> command;
DoWork();
if (command == "q")
{
break;
}
}
return 0;
}
Why does the insert not work?
Certainly would help if you wrote
currentFullState[context] = data;
instead of
currentFullState[context] == data;
Also
auto result = currentFullState.insert(std::make_pair(context, data));
should be preferred to
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
Slightly surprised that the second one compiles.
=========================================================================
The real reason the insert fails is that you are adding that key for the second time. This is the first time
if (currentFullState[context] == sDEFAULT_STRING)
operator[] on a map always adds the key to the map. This is why your second attempt to add with
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
fails, the key is already present. If you had written
currentFullState[context] = data;
Then it would work.

Data placed into array correctly but is not there when array is printed

I'm implementing my own hash table and I am running into the following problem: when I insert my node(s) into the table, they are not printed out when I loop through the array. I am using an array of arrays as the underlying data structure and the logic is as follows:
I pass my node to an insert function. This function, based on the
type of data in my node, calls the appropriate hash function provided
by the C++ STL.
Then, I mod the hash value returned by the size of
my hash table and use that to determine which array to place the
node.
I also have an array of arrays of booleans (the same size as my
hash table) that I use to check whether a specific spot in my hash
table already has data in it.
If it does, I simply keep looping till
an empty spot is found.
Like I said before, the problem is that the data is inputed correctly into the array (I've checked that with print statements), but when I print the array, nothing is outputted. I have also checked if my object is being constructed correctly (again, with print statements), but everything is looking fine. I've included the full code below. Any help would be greatly appreciated!
///////START OF NODE.H///////////
#ifndef NODE_H
#define NODE_H
#include <iostream>
template <typename T>
class HashTable;
template <typename T>
class Node
{
friend class HashTable<T>;
private:
T data;
public:
Node(T Data): data(Data)
{
std::cout << "In the node constructor" << std::endl;
}
Node()
{
decltype(data) {};
}
T getData() const
{
return data;
}
};
#endif
//////////////////////END OF NODE.H////////////////////
/////START OF HASHTABLE.H///////
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include "Node.h"
#include <iostream>
#include <array>
#include <functional>
#include <typeinfo>
#include <string>
const int TABLE_SIZE=5;
template <typename T>
class HashTable
{
private:
std::array<std::array<Node<T>, TABLE_SIZE>, TABLE_SIZE> hashTable;
std::array<std::array<bool, TABLE_SIZE>, TABLE_SIZE> spots;
public:
HashTable()
{
for(int index=0;index<spots.size();++index)
{
for(int position=0;position<spots.at(index).size();++position)
{
spots.at(index).at(position)=false;
}
}
}
int hashFunction(Node<T> Node)
{
auto key=Node.getData();
std::hash<decltype(Node.getData())> hash_function {};
int hash=hash_function(key);
if(hash < 0)
{
hash*=-1;
}
//std::cout << "The hash value return by the STL hash function for the key " << key << " is " << hash << std::endl;
if(hash > TABLE_SIZE)
{
hash%=TABLE_SIZE;
}
std::cout << "The hash value for the key " << key << " is " << hash << std::endl;
return hash;
}
void insert(Node<T> Node)
{
int hashValue=hashFunction(Node);
auto location=hashTable.at(hashValue);
std::cout << "Going to insert " << Node.getData() << std::endl;
for(int index=0;index<location.size();++index)
{
if(spots.at(hashValue).at(index)==false)
{
std::cout << "Found a spot that is not taken!" << std::endl;
std::cout << "The size of the data at the spot in the array before we insert is: " << location.at(index).getData().size() << std::endl;
location.at(index)=Node;
std::cout << "The size of the data at the spot in the array after we insert is: " << location.at(index).getData().size() << std::endl;
std::cout << "The data that is in the spot in the array: " << location.at(index).getData() << std::endl;
std::cout << std::endl;
spots.at(hashValue).at(index)=true;
break;
}
}
}
bool contains(Node<T> Node)
{
int hashValue=hashFunction(Node);
auto location=hashTable.at(hashValue);
auto result=find_if(begin(location), end(location), [Node] (const auto & element) {return element.getData()==Node.getData();});
if(result!=end(location))
{
return true;
}
return false;
}
int getSize() const
{
int size {};
for(int index=0;index<hashTable.size();++index)
{
size+=hashTable.at(index).size();
}
return size;
}
void print()
{
std::cout << "In the print function" << std::endl;
for(int index=0;index<hashTable.size();++index)
{
//std::cout << hashTable.at(index).size() << std::endl;
for(int position=0;position<hashTable.at(index).size();++position)
{
std::cout << hashTable.at(index).at(position).getData().size() << std::endl;
}
}
/*
for(int index=0;index<spots.size();++index)
{
for(int position=0;position<spots.at(index).size();++position)
{
if(spots.at(index).at(position)==true)
{
std::cout << "There should be some data here" << std::endl;
}
}
}
*/
}
};
#endif
////////////END OF HASHTABLE.H//////////
////////////START OF MAIN.CPP///////////
#include "HashTable.h"
#include <cstdlib>
#include <random>
#include <algorithm>
using namespace std;
int main()
{
HashTable<string> hash_table;
hash_table.insert(Node<string>("Java"));
hash_table.insert(Node<string>("C++"));
hash_table.insert(Node<string>("C#"));
hash_table.insert(Node<string>("Latex"));
hash_table.insert(Node<string>("Python"));
}
/////////////END OF MAIN.CPP/////////////
One error is in your insert(Node<T> Node) function on these line:
auto location=hashTable.at(hashValue);
//...
location.at(index) = Node;
The location should be a reference not a copy. What is happening is that you're making changes to a local location, and not the actual location that the hash table uses. Thus none of your changes "stick".
The line above should be this:
auto& location=hashTable.at(hashValue); // <-- note that auto is a reference
//...
location.at(index) = Node;
Now you are assigning the returned reference to a reference.
Also, I highly recommend you use a debugger, as this error could have been easily diagnosed if you stepped through your code to see what was being done.
in HashTable::insert this line:
auto location = hashTable.at(hashValue);
makes a copy of a Node. You then operate on and store in the copy, not the node in hashTable. Taking a reference to the node
auto & location = hashTable.at(hashValue);
should fix it.