This question already has answers here:
C++ remove_if on a vector of objects
(2 answers)
Closed 2 years ago.
I have the following class:
public:
Client(tcp::socket socket)
: socket_(std::move(socket))
{
}
void start();
int connectionId;
Than I have the following vector:
class Server {
public:
Server();
static std::vector<std::shared_ptr<Client>> Clients;
}
EDIT*
Can I do something like this:
for (int i = 0; i < Server::Clients.size(); ++i) {
if(Server::Clients[i]->connectionId == connectionId)
Server::Clients.erase(Server::Clients.begin()+i);
}
My question is how can I remove the shared pointer in Clients with a specific connectionId aka remove by value?
In C++17, you can do use std::remove_if. The following should work.
#include <algorithm>
Server &s = /* <getServerFromSomewhere> */;
auto new_end = remove_if(s.Clients.begin(), s.Clients.end(),
[] (std::shared_ptr<Client>& p) {
return p->connectionId == /* <someValueToBeRemoved> */;
}
);
s.Clients.resize(new_end - s.Clients.begin());
Use std::remove_if with a lambda as predicate and then container's method erase:
std::vector<std::shared_ptr<Client>> Clients;
int id{0}; // the id to be removed
Clients.erase(std::remove_if(Clients.begin(), Clients.end(), [id](const auto &entity) { return id == entity->connectionId; }), Clients.end());
If you don't have access to std::remove_if, you have to fall back to loop of a kind:
auto it = Clients.begin()
while (it != Clients.end())
{
if((*it)->connectionId = testValue)
{
// can connection id be non-unique?
it = Clients.erase(it);
//if yes, we have to save new iterator
continue;
}
it++;
}
If connectionId is unique use find_if to find that element and erase it, it's way cheaper.
If remove_if available, following code is possible, if it's advantage over first example is questionable.
auto new_end = remove_if(s.Clients.begin(), s.Clients.end(),
[=] (auto& ptr) {
return ptr->connectionId == testValue; /* value to be removed */
}
erase(new_end, Clients.end()); // remove tail of vector that is now consisting of empty elements.
Clients better be a deque than a vector, that way erasing an element from it won't cause the "tail" part of Clients list to be copied when every removal is done.
Suppose we have a data structure that is a key-value map, where the key itself is again a key-value map. For example:
map<map<string,string>>, string>
Now, suppose that we want to query all top-level key/values in this map matching a certain subset of the key-values of the key. Example:
map = { { "k1" : "v1", "k2 : "v2" } : "value1",
{ "k1" : "v3", "k2 : "v4" } : "value2",
{ "k1" : "v1", "k2 : "v5" } : "value3"
}
And our query is "give me all key-values where key contains { "k1" : "v1" } and it would return the first and third value. Similarly, querying for { "k1" : "v3", "k2" : "v4" } would return all key-values that have both k1=v3 and k2=v4, yielding the second value. Obviously we could search through the full map on every query, but I'm looking for something more efficient than that.
I have looked around, but can't find an efficient, easy-to-use solution out there for C++. Boost multi_index does not seem to have this kind of flexibility in querying subsets of key-value pairs.
Some databases have ways to create indices that can answer exactly these kind of queries. For example, Postgres has GIN indices (generalized inverted indices) that allow you to ask
SELECT * FROM table WHERE some_json_column #> '{"k1":"v1","k2":"v2"}'
-- returns all rows that have both k1=v1 and k2=v2
However, I'm looking for a solution without databases just in C++. Is there any library or data structure out there that can accomplish something like this? In case there is none, some pointers on a custom implementation?
I would stay with the database index analogy. In that analogy, the indexed search does not use a generic k=v type search, but just a tuple with the values for the elements (generally columns) that constitute the index. The database then reverts to scans for the other k=v parameters that are not in the index.
In that analogy, you would have a fixed number of keys that could be represented as an array or strings (fixed size). The good news is that it is then trivial to set a global order on the keys, and thanks to the std::map::upper_bound method, it is also trivial to find an iterator immediately after a partial key.
So getting a full key is immediate: just extract it with find, at or operator []. And getting all elements for a partial key is still simple:
find an iterator starting above the partial key with upper_bound
iterate forward while the element matches the partial key
But this require that you change your initial type to std::map<std::array<string, N>, string>
You could build an API over this container using std::map<string, string> as input values, extract the actual full or partial key from that, and iterate as above, keeping only elements matching the k,v pairs not present in index.
You could use std::includes to check if key maps include another map of queried key-value pairs.
I am unsure how to avoid checking every key-map though. Maybe other answers have a better idea.
template <typename MapOfMapsIt, typename QueryMapIt>
std::vector<MapOfMapsIt> query_keymap_contains(
MapOfMapsIt mom_fst,
MapOfMapsIt mom_lst,
QueryMapIt q_fst,
QueryMapIt q_lst)
{
std::vector<MapOfMapsIt> out;
for(; mom_fst != mom_lst; ++mom_fst)
{
const auto key_map = mom_fst->first;
if(std::includes(key_map.begin(), key_map.end(), q_fst, q_lst))
out.push_back(mom_fst);
}
return out;
}
Usage:
typedef std::map<std::string, std::string> StrMap;
typedef std::map<StrMap, std::string> MapKeyMaps;
MapKeyMaps m = {{{{"k1", "v1"}, {"k2", "v2"}}, "value1"},
{{{"k1", "v3"}, {"k2", "v4"}}, "value2"},
{{{"k1", "v1"}, {"k2", "v5"}}, "value3"}};
StrMap q1 = {{"k1", "v1"}};
StrMap q2 = {{"k1", "v3"}, {"k2", "v4"}};
auto res1 = query_keymap_contains(m.begin(), m.end(), q1.begin(), q1.end());
auto res2 = query_keymap_contains(m.begin(), m.end(), q2.begin(), q2.end());
std::cout << "Query1: ";
for(auto i : res1) std::cout << i->second << " ";
std::cout << "\nQuery2: ";
for(auto i : res2) std::cout << i->second << " ";
Output:
Query1: value1 value3
Query2: value2
Live Example
I believe the efficiency of different methods will depend on actual data. However, I would consider making a "cache" of iterators to outer map elements for particular "kX","vY" pairs as follows:
using M = std::map<std::map<std::string, std::string>, std::string>;
M m = {
{ { { "k1", "v1" }, { "k2", "v2" } }, "value1" },
{ { { "k1", "v3" }, { "k2", "v4" } }, "value2" },
{ { { "k1", "v1" }, { "k2", "v5" } }, "value3" }
};
std::map<M::key_type::value_type, std::vector<M::iterator>> cache;
for (auto it = m.begin(); it != m.end(); ++it)
for (const auto& kv : it->first)
cache[kv].push_back(it);
Now, you basically need to take all searched "kX","vY" pairs and find the intersection of cached iterators for them:
std::vector<M::key_type::value_type> find_list = { { "k1", "v1" }, { "k2", "v5" } };
std::vector<M::iterator> found;
if (find_list.size() > 0) {
auto it = find_list.begin();
std::copy(cache[*it].begin(), cache[*it].end(), std::back_inserter(found));
while (++it != find_list.end()) {
const auto& temp = cache[*it];
found.erase(std::remove_if(found.begin(), found.end(),
[&temp](const auto& e){ return std::find(temp.begin(), temp.end(), e) == temp.end(); } ),
found.end());
}
}
The final output:
for (const auto& it : found)
std::cout << it->second << std::endl;
gives value3 in this case.
A live demo: https://wandbox.org/permlink/S9Zp8yofSvjfLokc.
Note that the complexity of the intersection step is quite large, since cached iterators are unsorted. If you use pointers instead, you can sort the vectors or store the pointers in a map instead, which would allow you to find intersections much faster, e.g., by using std::set_intersection.
You can do it with as single (partial) pass through each element with an ordered query, returning early as much as possible. Taking inspiration from std::set_difference, we want to know if query is a subset of data, which lets us select entries of the outer map.
// Is the sorted range [first1, last1) a subset of the sorted range [first2, last2)
template<class InputIt1, class InputIt2>
bool is_subset(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
{
while (first1 != last1) {
if (first2 == last2) return false; // Reached the end of data with query still remaing
if (*first1 < *first2) {
return false; // didn't find this query element
} else {
if (! (*first2 < *first1)) {
++first1; // found this query element
}
++first2;
}
}
return true; // reached the end of query
}
// find every element of "map-of-maps" [first2, last2) for which the sorted range [first1, last1) is a subset of it's key
template<class InputIt1, class InputIt2, class OutputIt>
OutputIt query_data(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first)
{
auto item_matches = [=](auto & inner){ return is_subset(first1, last1, inner.first.begin(), inner.first.end()); };
return std::copy_if(first2, last2, d_first, item_matches);
}
std::map is implemented as a balanced binary tree which has O(nlgn) look-up. What you need instead, is std::unordered_map which is implemented as a hash-table, that is O(1) look-ups.
Now let me rephrase your wording, you want to:
And our query is "give me all key-values where key contains { "k1" : "v1" } and it would return the first and third value.
Which translates to:
If the key-value pair given is in the inner map, give me back its value.
Essentially what you need is a double look-up which std::unordered_map excel at.
Here is a code spinet that solves your problem with the standard library (no fancy code required)
#include <iostream>
#include <unordered_map>
#include <string>
int main() {
using elemType = std::pair<std::string, std::string>;
using innerMap = std::unordered_map<std::string, std::string>;
using myMap = std::unordered_map<std::string, innerMap>;
auto table = myMap{ { "value1", { {"k1", "v1"}, {"k2", "v2"} } },
{ "value2", { {"k1", "v3"}, {"k2", "v4"} } },
{ "value3", { {"k1", "v1"}, {"k2", "v5"} } } };
//First we set-up a predicate lambda
auto printIfKeyValueFound = [](const myMap& tab, const elemType& query) {
// O(n) for the first table and O(1) lookup for each, O(n) total
for(const auto& el : tab) {
auto it = el.second.find(query.first);
if(it != el.second.end()) {
if(it->second == query.second) {
std::cout << "Element found: " << el.first << "\n";
}
}
}
};
auto query = elemType{"k1", "v1"};
printIfKeyValueFound(table, query);
Output: Value3, Value1
For queries of arbitrary size you can:
//First we set-up a predicate lambda
auto printIfKeyValueFound = [](const myMap& tab, const std::vector<elemType>& query) {
// O(n) for the first table and O(n) for the query O(1) search
// O(n^2) total
for(const auto& el : tab) {
bool found = true;
for(const auto& queryEl : query) {
auto it = el.second.find(queryEl.first);
if(it != el.second.end() && it->second != queryEl.second) {
found = false;
break;
}
}
if(found)
std::cout << el.first << "\n";
}
};
auto query = std::vector<elemType>{ {"k1", "v1"}, {"k2", "v2"} };
output Value1
I'm new to C++ and predicates but I'm running into a problem. I'm trying to check if all keys in an unordered_map exist in a set or even keys in another map with different value type for that matter.
Essentially, [key in set_ for key in map_.keys()] OR [key in map2_ for key in map1_.keys()] in python.
one approach is the following:
for( auto itr = map_.begin(); itr != map_.end(); ++itr ) {
if( set_.find( itr->first ) == set_.end() ) return false;
}
But I want to use std::all_of or std::equal to achieve the same. Here's what I have so far:
some_function() {
//set_ and map_ in scope
areEqual = std::equal( map_.begin(), map_.end(), set_,
[]( auto itr ){ return set_.find( itr->first ) != set_.end(); } );
}
compiler complains that set_ is out of scope in return set_.find(...)...
I also tried
class checker {
public:
bool operator()( map_itr itr )
{ return set_.find( itr->first ) != set_.end(); }
};
but ran into the same problem.
Any help or pointers is greatly appreciated :)
Lambdas can't access variables declared in its scope by default. In order to access your set_ variable from the lambda function, you should capture it in the lambda function.
The capture list is defined between the [] characters at the beginning of the lambda function.
In this case, I would use [&] which captures all objects from the scope by reference, including your set_ variable.
some_function() {
areEqual = std::equal( map_.begin(), map_.end(), set_,
[&]( auto itr ){ return set_.find( itr->first ) != set_.end(); } );
}
You should read the Lambda expressions page on cppreference for further information.
I have a Json record with nested object and object arrays, the keys in those field contain spaces, I want to change all spaces to _, so I have to iterate all keys in the json object.
My idea is to write a depth first search to iterate all nested keys using ConstMemberIterator, my question is how can I change the key by given its iterator?
The example below represents my idea:
void DFSReplaceSpaceInKeys(Value::ConstMemberIterator& itr) {
// Replace space in nested key
std::string s(itr->name.GetString());
std::replace(s.begin(), s.end(), ' ', '_');
// set new string to key
itr->name.SetString(s, ?.GetAllocator()); // <----- How can I get the document allocator?
std::cout << "new key: " << itr->name.GetString() << std::endl;
// recursive call in DFS
if (itr->value.GetType() == Type::kObjectType) {
DFSReplaceSpaceInKeys(itr->value.GetObject().MemberBegin());
}
}
A Json record example:
{
"a": {"b": [{"c": [...]}, {...}]
}
You can pass an allocator as parameter. I also think you should better pass Value& to represent a node.
void DFSReplaceSpaceInKeys(Value& value, Value::AllocatorType& allocator) {
if (value.IsObject()) {
for (Value::ConstMemberIterator itr = value.MemberBegin(); itr != MemberEnd(); ++itr)
{
// Modify itr->name here...
DFSReplaceSpaceInKeys(itr->value, allocator);
}
}
else if (value.IsArray()) {
for (Value::ConstIterator itr = value.Begin(); itr != value.End(); ++itr)
DFSReplaceSpaceInKeys(*itr, allocator);
}
}
// ...
Document d;
DFSReplaceSpaceInKeys(d, d.GetAllocator());
If you only need to do the task as mentioned, you may just use the SAX API, which can be easier and faster. Check capitalize example.
rapidjson::Document::AllocatorType& allocator = doc.GetAllocator();
auto news_obj= news_info["news_feature"].GetObject();
auto title_keyword = news_obj.FindMember ("title_keyword");
if (title_keyword != news_obj.MemberEnd()) {
title_keyword->name.SetString ("title_keywords", allocator);
}
typedef std::vector<UCHAR> RESPONSE_BUFFER;
typedef TimedHashMap<int, RESPONSE_BUFFER*> TimeResponseHashMap;
Inner map prototype has "integer" as key and "pointer to a vector of chars" as mapped value.
TimeResponseHashMap* inner_pending_response_map;
Outer map is a map of maps. it has "integer" as key and "pointer to inner map" as mapped value.
std::map<int, TimeResponseHashMap* > outer_pending_response_map;
I insert like this:
Inner map is a userdefined map, hence it has a different format.
inner_pending_response_map->Insert((int)s16MessageID, &resp_buffer, expirytime);
outer_pending_response_map.insert(make_pair((int)s16SessionID,
inner_pending_response_map));
TimeResponseHashMap provides the user defined interface "Find" to access members of the map.
template <typename Key, typename ElementObject>
THM_ERROR TimedHashMap<Key, ElementObject>::Find(const Key& k, ElementObject& e)
{
typename hash_map<Key, BaseElement_*, dw_hash<Key>, dw_equal_to<Key> >::iterator itr;
try
{
itr = h_->find(k);
} catch ( ... )
{
return E_INTERNAL_ERROR;
}
if ( itr == h_->end() )
{
e = NULL;
return E_ITEM_NOT_FOUND;
}
e = itr->second->e_;
return E_SUCCESS;
}
I have both the keys and now I need to access the "mapped value" of the inner map. The inner map uses the above Find() function to search. I need to pass RESPONSE_BUFFER variable as the second parameter to the Find() function.
I'm trying to extract like this which gives a wrong output:
RESPONSE_BUFFER resp_buffer;
ExtractFragmentResponse(u16Key1, u16Key2, &resp_buffer);
Definition of ExtractFragmentResponse is below:
STATUS
C_THREAD::ExtractFragmentResponse(USHORT u16SessionID, USHORT u16MessageID,
RESPONSE_BUFFER* resp)
{
(((outer_pending_response_map.find(u16SessionID))->second)->Find((int)u16MessageID, resp))
}
resp is not giving me correct data.
How can it be done?
This may not directly answer the question, but it does bring up why the code used is highly faulty and should be broken up into several lines.
STATUS
C_THREAD::ExtractFragmentResponse(USHORT u16SessionID, USHORT u16MessageID,
RESPONSE_BUFFER* resp)
{
(((outer_pending_response_map.find(u16SessionID))->second)->Find((int)u16MessageID, resp))
}
Let's say this did actually "work". There is a major problem with it, regardless. The issue is this:
outer_pending_response_map.find(u16SessionID)
What happens if find doesn't find the entry u16SessionID? You now have been returned outer_pending_response_map.end(). When this return value is used like this:
outer_pending_response_map.end()->second
boom, your dead. That line attempts to use an invalid iterator, which is undefined behavior.
What you should do is this:
std::map<int, TimeResponseHashMap* >::iterator it1 = outer_pending_response_map.find(u16SessionID);
if ( it1 != outer_pending_response_map.end())
(it1->second)->Find((int)u16MessageID, resp);
else
{
// you fill in what happens if the find fails
}
To debug your issue, you can then further break up the lines to ensure what you're getting is valid:
std::map<int, TimeResponseHashMap* >::iterator it1 = outer_pending_response_map.find(u16SessionID);
if ( it1 != outer_pending_response_map.end())
{
TimeResponseHashMap *theMap = it1->second;
theMap->Find((int)u16MessageID, resp);
}
else
{
// you fill in what happens if the find fails
}
The code above saves it1->second to a value (theMap) that you can inspect easily to see if it is correct.