Continuing my previous question here, Now I want to insert the keys and values present in the below json into a std::vector<std::pair<std::string, std::vector<uint64_t>>> vec;
Keys here are this strings: 12CUDzb3oe8RBQ4tYGqsuPsCbsVE4KWfktXRihXf8Ggq , 12ashmTiFStQ8RGUpi1BTCinJakVyDKWjRL6SWhnbxbT
values corresponding them are list:[20964,347474, 34747],[1992,1993,109096]
This is the json which is response from query.
j = {
"12CUDzb3oe8RBQ4tYGqsuPsCbsVE4KWfktXRihXf8Ggq": [
20964,
347474,
347475
],
"12ashmTiFStQ8RGUpi1BTCinJakVyDKWjRL6SWhnbxbT": [
1992,
1993,
109096
]
}
To try first I have tried to insert only first element's key and value. It is working correctly.
std::vector<std::pair<std::string, std::vector<uint64_t>>> vec;
auto key = j.begin().key();
auto value = j.begin().value();
vec.push_back(std::make_pair(key, value));
Now I am trying this way to insert all the key values in vector
std::vector<std::pair<std::string, std::vector<uint64_t>>> vec;
int i = 0;
while ((j.begin() + i) != j.end()) {
auto key = (j.begin() + i).key();
auto value = (j.begin() + i).value();
vec.push_back(std::make_pair(key, value));
i++;
}
I am getting the error:
[json.exception.invalid_iterator.209]
cannot use offsets with object iterators
Can someone please what is the correct way of doing this ?
I think you're over complicating this. You can iterate over a json object the same way you would any other container using a for loop:
#include "nlohmann/json.hpp"
#include <iostream>
int main()
{
nlohmann::json j = nlohmann::json::parse(R"({
"12CUDzb3oe8RBQ4tYGqsuPsCbsVE4KWfktXRihXf8Ggq": [
20964,
347474,
347475
],
"12ashmTiFStQ8RGUpi1BTCinJakVyDKWjRL6SWhnbxbT": [
1992,
1993,
109096
]
})");
std::vector<std::pair<std::string, std::vector<uint64_t>>> vec;
for (auto it = j.begin(); it != j.end(); ++it)
{
vec.emplace_back(it.key(), it.value());
}
for (auto& it : vec)
{
std::cout << it.first << ": ";
for (auto& value : it.second)
{
std::cout << value << ", ";
}
std::cout << "\n";
}
}
If you don't care about the order of items (JSON keys are unordered anyway and nlohmann doesn't preserve the order by default) then you can do this in a one liner:
std::map<std::string, std::vector<uint64_t>> vec = j;
Related
Hey my mentor(kinda) gave me this task and i am having trouble solving it .
So basically i am getting const vector<string> &data full of string and i need to check their place, like where are they in that vector so here is an example:
getting an input with: data={"chair","desk","table","chair","chair","desk"}
my output should be:{"chair" ->{0,3,4},"desk"->{1,5} , "table"->{2}}
so what i did is :
map<string, set<size_t>> index(const vector<string> & data) noexcept {
map<string, set<size_t>> res; // thats gon be my return
if (data.empty()){
return res;
}
for (auto it = data.begin(); it != data.end(); it++) {
if(res.find(*it)==res.end()){
//so basically when we are the first , and nothing is there so
// it should insert for example =chair , and a 0
res.insert(std::make_pair(*it, 0)); // i tried it like that it doesnt work
}else{
// when the string "chair is already in there i want to append to the set the current index where we are and thats what i dont know how
}
}
return res;
}
how to get the index of the current string and append it to my set<size_t> so that is works as mentioned?
You can implement this quite easily by using an index-based loop:
map<string, set<size_t>> index(const vector<string> & data)
{
map<string, set<size_t>> res;
for (std::size_t i = 0u; i < data.size(); ++i)
res[ data[i] ].insert(i);
// ^string^ ^index
return res;
}
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
std::map<std::string, std::set<size_t>> index(const std::vector<std::string>& data) noexcept
{
std::map<std::string, std::set<size_t>> res;
for (size_t idx = 0u; idx < data.size(); idx++)
{
res[data[idx]].insert(idx);
}
return res;
}
int main()
{
const std::vector<std::string> data={"chair","desk","table","chair","chair","desk"};
auto map = index(data);
for( auto it = map.begin(); it != map.end(); ++it )
{
std::cout << (*it).first << " ->";
for( auto it_id = (*it).second.begin(); it_id != (*it).second.end(); it_id++)
{
std::cout << " " << *it_id;
}
std::cout << "\n";
}
}
So your problem can be solved with a oneline as seen in the index function - but why ?
There is a reason youre getting all these different std container types as input - each of them have some guarantees as can be looked up in the documentation.
vector elements are continous in memory (sequence container).
while map and set are associative and have unique keys.
Consult the documentation for container properties (i linked to the vector - but the rest are also present in the tree to the left).
http://www.cplusplus.com/reference/vector/vector/
The lesson is knowing which tool(container) to use to solve specific tasks
And once you have that nailed .. making sure items are unique or how to sequence every odd element in a vector should be a breeze.
i'm new to C++. My program is a quiz game which user can choose category and level for the questions. At first, i use the struct data type
struct QuestionInfo
{
string category;
string level;
string question;
string answer;
};
then
vector<QuestionInfo> vec;
The idea of this part is to store the info of the question include (category, level, question and answer) to each element.
Then after building menu and the output questions UI, i go to the filters
void category_filter()
{
for (unsigned int i = 0; i < vec.size(); i ++)
{
if (category_choice != vec[i].category)
vec.erase(vec.begin() + i );
}
}
Void level_filter()
{
for (unsigned int i = 0; i < vec.size(); i ++)
{
if (level_choice != vec[i].level)
vec.erase(vec.begin() + i );
}
}
So the idea of the filters is to delete the elements which not contain the matched category and level. But the output questions did not match with the category and the level i had choose before. I'm not sure what I'm doing wrong.
Let me explain you the problem with my example. Suppose you have a vector of 10 elements, valid indexes are 0 till 9 elements. You have to erase 5th element i == 4. You erase it, then 6th element with index 5 moves to place of 5th elements with index 4. After that you increase i in for, it becomes 5. Thus you skip previous 6th element, that is now 5th with index 4.
You may fix your code like below, moving i ++ to the condition.
for (unsigned int i = 0; i < vec.size(); ) {
if (category_choice != vec[i].category)
vec.erase(vec.begin() + i );
else
i ++;
}
The preferable solution in C++ way is demonstrated by #Jonathan.
You're getting tripped up by not accounting for the indexing shift that occurs when you erase an element. I personally would rely on remove_if and erase with a lambda to accomplish this:
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return category_choice != i.category; }, end(vec));
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return level_choice != i.level; }, end(vec));
Alternatively you might consider combining them for a bit of speed improvement:
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return category_choice != i.category || level_choice != i.level; }, end(vec));
You might want to remove_if + erase:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main()
{
struct QuestionInfo
{
std::string category;
std::string level;
std::string question;
std::string answer;
QuestionInfo(std::string category, std::string level, std::string question, std::string answer) :
category(category), level(level), question(question), answer(answer) {}
};
std::vector<QuestionInfo> vec;
std::string category_choice = "cat1";
std::string level_choice = "lev1";
vec.push_back(QuestionInfo("cat1", "lev1", "q1", "a1"));
vec.push_back(QuestionInfo("cat1", "lev2", "q2", "a2"));
vec.push_back(QuestionInfo("cat2", "lev1", "q3", "a3"));
vec.push_back(QuestionInfo("cat2", "lev2", "q4", "a4"));
std::cout << "\nNot filered" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
auto filter_category = std::remove_if(vec.begin(), vec.end(), [&](auto const &info) {return category_choice != info.category; });
vec.erase(filter_category, vec.end());
std::cout << "\nFilered by category" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
auto filter_level = std::remove_if(vec.begin(), vec.end(), [&](auto const &info) {return level_choice != info.level; });
vec.erase(filter_level, vec.end());
std::cout << "\nFiltered by level" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
system("pause");
return 0;
}
As mentioned by others, the remove_if + erase is a standard and expressive way to achieve what you want. But you may also consider non-destructive filtering with a copy_if into a new container, or even without using any additional storage with Boost.Range adaptor boost::adaptors::filtered or boost::filter_iterator. Look here for examples.
Let's say I have the following object:
vector<string> data = {"12","12","12","12","13","14","15", "15", "15", "15", "18"};
I'm trying to find the first non-repeating entry in the data object.
For example, data.find_first_not_of(data.at(0)); this would work if data is of string type only (no container).
How can I achieve the same thing with an object of type vector.
I looked at adjacent_find and find_if_not from the algorithm library, but to no avail.
Your suggestions are much appreciated.
What problem did you have with adjacent_find? You should be able to use that with an inverse predicate:
std::vector<std::string> data = {"12","12","12","12","13","14","15", "15", "15", "15", "18"};
// Sort data here if necessary
auto itr = std::adjacent_find(data.cbegin(), data.cend(), std::not_equal_to<std::string>{});
if (itr != data.cend()) {
std::cout << "First mismatch: " << *itr << " " << *std::next(itr) << std::endl;
} else {
std::cout << "All elements equal" << std::endl;
}
Wandbox
Since you have to go through the list at least once, and you don't know when or where you will encounter the duplicate of a number (if there is one), one way to solve this is to first gather "statistics" and then from what you've gathered you can determine the first non-duplicate.
Here is an example using std::unordered_map:
#include <algorithm>
#include <unordered_map>
#include <iostream>
#include <vector>
#include <string>
// struct to hold some information on the numbers
struct info
{
std::string number;
int count;
int position;
info(const std::string n, int c, int p) : number(n), count(c), position(p) {}
};
int main()
{
std::vector<std::string> data = {"12","12","12","12","13","14","15", "15", "15", "15", "18"};
std::unordered_map<std::string, info> infoMap;
std::vector<info> vInfo;
int pos = 0;
// loop for each data element
std::for_each(data.begin(), data.end(), [&](const std::string& n)
{
// insert entry into the map
auto pr = infoMap.insert(std::make_pair(n, info(n, 0, pos)));
// bump up the count for this entry.
++pr.first->second.count;
// bump up the postion number
++pos;
});
// create a vector of the information with a count of 1 item.
std::for_each(infoMap.begin(), infoMap.end(), [&](std::unordered_map<std::string, info>::value_type& vt) { if (vt.second.count == 1) vInfo.push_back(vt.second); });
// sort this by position
std::sort(vInfo.begin(), vInfo.end(), [&](const info& pr1, const info &pr2){return pr1.position < pr2.position; });
// output the results
if ( vInfo.empty() )
std::cout << "All values are duplicated\n";
else
std::cout << "The first number that isn't repeated is " << vInfo.front().number << "\n";
}
Live Example
First, we just simply go through all the entries in the vector and just tally up the count for each item. In addition, we store the position in the original list of where the item was found.
After that we filter out the ones with a count of exactly 1 and copy them to a vector. We then sort this vector based on the position they were found in the original list.
I have a map defined as std::map<std::string, textInfo> tempMap;
the textInfo class has some attributes as textsize,textcolor,textfont etc..
I want to select an item from this map that matches with a given value to an attribute in textInfo class.
for example if the Map contains
<"A",textInfo("10","Red","Verdana")>
<"B",textInfo("12","Green","Timesnewroman")>
<"C",textInfo("11","Blue","Cambria")>
I want to select the item that contains "Cambria" in it textfont attribute.
<"C",textInfo("11","Blue","Cambria")>
std::find_if should work for your needs.
Sample program:
#include <iostream>
#include <map>
#include <algorithm>
struct textInfo
{
std::string textsize;
std::string textcolor;
std::string textfont;
};
int main()
{
std::map<std::string, textInfo> m =
{
{"A", {"10","Red","Verdana"}},
{"B", {"12","Green","Timesnewroman"}},
{"C", {"11","Blue","Cambria"}}
};
auto iter = std::find_if(m.begin(),
m.end(),
[](std::pair<std::string, textInfo> const& item)
{ return (item.second.textfont == "Cambria");});
if ( iter != m.end() )
{
auto& item = iter->second;
std::cout << item.textsize << ", " << item.textcolor << ", " << item.textfont << std::endl;
}
}
Output:
11, Blue, Cambria
You can only access maps directly via a key, here your std::string. To search for a value or even a variable inside a value like it's the case here you have to iterate over the whole map.
std::map<std::string, textInfo>::const_iterator it = tempMap.begin();
for (; it != tempMap.end(); ++it)
{
if (0 == tempMap[(*it)].textfont.equals("Cambria")) // You could use == operator if it's a std::string
{
break; // found
}
}
// Do something with the found item. If the iterator is tempMap.end(), nothing found!
Look here for more informations.
I want to collect keys of the same value in a map. What is the easiest way to do it using vector? That means all the keys having the same value can be collected in a vector.
You will have to do a linear search over the whole container, which is O(N).
std::vector<Value> values;
std::for_each(map.begin(), map.end(),
[&](std::map<Key,Value>::value_type const & x) {
if (x.second == value)
values.push_back(x.first);
});
If you want to extract all keys for which the value is not unique, the complexity of the code is higher, and you will need additional data, but you could do something like this:
std::map<Value, std::pair<Key, bool>> tracker;
// Maps a 'Value' to the first 'Key' that had it, and a 'bool'
// identifying if it has already been inserted into the vector.
std::vector<Key> keys;
for_each(m.begin(), m.end(),
[](std::map<Key, Value>::value_type const& x) {
auto r = tracker.insert(std::make_pair(x.second,
std::make_pair(x.first, false));
if (!r.second) {
// Not the first time we saw this value
if (!r.first->second) {
// First key not already inserted, insert now and update flag
keys.push_back(r.first);
r.first->second = true;
}
keys.push_back(x.first);
}
});
Although in real code I would avoid using std::pair and would create a named type that makes the code simpler to read. In the code above it is not obvious what all those first and second mean…
A different alternative, probably more efficient (measure and profile) would be to use transform to create a vector where the elements are swapped and then iterate over that vector extracting the values of interest.
You can do it the following way
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main()
{
std::map<int, std::string> m
{
{ 1, "Monday" }, { 2, "Tuesday" }, { 9, "Monday" }
};
std::vector<int> v;
size_t n = 0;
std::string s( "Monday" );
for ( const auto &p : m )
{
if ( p.second == s ) ++n;
}
v.reserve( n );
for ( const auto &p : m )
{
if ( p.second == s ) v.push_back( p.first );
}
for ( const auto &x : v ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
The output is
1 9
You can substitute the range based for statements for correspondingly std::count_if and std::for_each algorithms along with lambda expressions. But in my opiniion for this simple task it is better to use the range based for statements.
Where you are creating the map, consider creating a unordered_multimap with the key and value of the original map swapped.