c++ nlohmann json - how to iterate / find a nested object - c++

I am trying to iterate over a nested json, using nlohmann::json. My json object is below:
{
"one": 1,
"two": 2
"three": {
"three.one": 3.1
},
}
I am trying to iterate and /or find nested objects. But, it seems there is no default support for it. It looks like I have to iterate over each sub-object by creating another loop, or call the fn recursively for every sub-object.
My following piece of code, and its result indicate, that only top level iteration possible.
void findNPrintKey (json src, const std::string& key) {
auto result = src.find(key);
if (result != src.end()) {
std::cout << "Entry found for : " << result.key() << std::endl;
} else {
std::cout << "Entry not found for : " << key << std::endl ;
}
}
void enumerate () {
json j = json::parse("{ \"one\" : 1 , \"two\" : 2, \"three\" : { \"three.one\" : 3.1 } } ");
//std::cout << j.dump(4) << std::endl;
// Enumerate all keys (including sub-keys -- not working)
for (auto it=j.begin(); it!=j.end(); it++) {
std::cout << "key: " << it.key() << " : " << it.value() << std::endl;
}
// find a top-level key
findNPrintKey(j, "one");
// find a nested key
findNPrintKey(j, "three.one");
}
int main(int argc, char** argv) {
enumerate();
return 0;
}
and the output:
ravindrnathsMBP:utils ravindranath$ ./a.out
key: one : 1
key: three : {"three.one":3.1}
key: two : 2
Entry found for : one
Entry not found for : three.one
So, is there a recursive iteration available, or do we have to do this ourselves, using is_object() method?

Indeed, iteration does not recurse and there is no library function for this (yet). What about:
#include "json.hpp"
#include <iostream>
using json = nlohmann::json;
template<class UnaryFunction>
void recursive_iterate(const json& j, UnaryFunction f)
{
for(auto it = j.begin(); it != j.end(); ++it)
{
if (it->is_structured())
{
recursive_iterate(*it, f);
}
else
{
f(it);
}
}
}
int main()
{
json j = {{"one", 1}, {"two", 2}, {"three", {"three.one", 3.1}}};
recursive_iterate(j, [](json::const_iterator it){
std::cout << *it << std::endl;
});
}
The output is:
1
"three.one"
3.1
2

This is a spin off from the accepted answer, which gives you the added benefit of having the parent keys (in addition to the iterator) as you "walk" the json tree.
The parent keys are provided in a list format to easily iterate over them directly. I've also provided the means to convert that list of strings into a "nested json key" (i.e. a json_pointer). That is an object you can use to directly access that k/v pair when performing assorted operations built into nlohmann::json.
Utility functions
#include <string>
#include <list>
#include "nlohmann.hpp"
using JsonIter = nlohmann::json::const_iterator;
typedef std::list<std::string> JsonKeys;
std::string toJsonStringKey( const JsonKeys &keys )
{
static const std::string JSON_KEY_DELIM( "/" );
std::string s;
for( auto k : keys ) s.append( JSON_KEY_DELIM + k );
return s;
}
nlohmann::json::json_pointer toJsonPointerKey( const JsonKeys &keys )
{ return nlohmann::json::json_pointer( toJsonStringKey( keys ) ); }
nlohmann::json::json_pointer toJsonPointerKey(
const JsonKeys &parentKeys, JsonIter it )
{
JsonKeys allKeys( parentKeys );
allKeys.push_back( it.key() );
return nlohmann::json::json_pointer( toJsonStringKey( allKeys ) );
}
typedef std::function< void( const JsonKeys &parentKeys,
nlohmann::json::const_iterator it )> WalkJsonCallBack;
void walkJson( const nlohmann::json &jsonObj, JsonKeys &parentKeys,
WalkJsonCallBack callback )
{
for( auto it( jsonObj.begin() ); it != jsonObj.end(); ++it )
{
if( it->is_structured() )
{
parentKeys.push_back( it.key() );
walkJson( *it, parentKeys, callback );
parentKeys.pop_back();
}
else callback( parentKeys, it );
}
}
Example implementation
const nlohmann::json parsed( nlohmann::json::parse( raw ) );
JsonKeys parentKeys;
walkJson( parsed, parentKeys, []( const JsonKeys &parentKeys, JsonIter it )
{
// INSERT YOUR CODE HERE
// Example of getting a pointer key..
const auto key( toJsonPointerKey( parentKeys, it ) );
// Now, do whatever with that key...
});
Sample Data
And here's the op's sample data, after adding a few more fields and nestings:
const std::string testData(
"{ \"one\" : 1 , \"two\" : 2, "
"\"three\" : { "
" \"three.one\" : 3.1, "
" \"three.two\" : { \"three.two.one\" : 3.21, \"three.two.two\" : 3.22 }, "
" \"three.three\": { \"three.three.one\" : 3.31, \"three.three.two\" : 3.32 }, "
" \"three.four\": 3.4, "
" \"three.five\": { \"three.five.one\" : 3.51, \"three.five.two\" : 3.52 } "
"}, "
"\"four\" : 4"
"} " );

Related

Why is count_if giving me the total of texts

I was testing the following code, and a bit perplexed as to why count_if is returning me the total of texts?
Match function that takes string of Text as argument and returns true is the Text size is 4
bool Match(string Text)
{
if (Text.size() == 4)
return true;
}
numMatchwes produces the total number of Text in a vector
int numMatches(vector<string>Texts, bool (*Match)(string Text)) // Texts is an array
{
int count = 0;
for (int i = 0; i < Texts.size(); i++)
{
if (Match(Texts[i]) == 1)
// checking every string in vector and returns true
count++;
}
return count;
}
The main function
int main()
{
vector<string> texts;
texts.push_back("Bing");
texts.push_back("Pony");
texts.push_back("Mil");
texts.push_back("Sillty");
texts.push_back("Ballz");
texts.push_back("Mars");
cout << Match("Sind") << endl;
cout << "Matches are: " << numMatches(texts, Match) << endl;
cout << endl;
int num = count_if(texts.begin(), texts.end(), Match); // count_if is STL function
cout << num << endl;
}
Now I’m confused as to why count_if is giving me the total of texts?
The function Match has undefined behavior in case when the passed string does not have a length equal to 4.
Define it the following way
bool Match( const std::string &Text )
{
return Text.size() == 4;
}
Correspondingly the function numMatches can be defined the following way
auto numMatches( const std::vector<std::string> &Texts, bool Match(const std::string & ) )
{
std::vector<std::string>::size_type count = 0;
for ( const auto &s : Texts )
{
if ( Match( s ) ) ++count;
}
return count;
}
Here is your updated program.
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
auto numMatches( const std::vector<std::string> &Texts, bool Match(const std::string & ) )
{
std::vector<std::string>::size_type count = 0;
for ( const auto &s : Texts )
{
if ( Match( s ) ) ++count;
}
return count;
}
bool Match( const std::string &Text )
{
return Text.size() == 4;
}
int main()
{
std::vector<std::string> texts;
texts.push_back( "Bing" );
texts.push_back( "Pony" );
texts.push_back( "Mil" );
texts.push_back( "Sillty" );
texts.push_back( "Ballz" );
texts.push_back( "Mars" );
std::cout << std::boolalpha << Match( "Sind" ) << '\n';
std::cout << "Matches are: " << numMatches( texts, Match ) << '\n';
std::cout << '\n';
auto num = std::count_if( std::begin( texts ), std::end( texts ), Match );
std::cout << num << std::endl;
return 0;
}
The program output is
true
Matches are: 3
3

How to get lower_bound for map in a specific range?

I want to find the lower_bound for my target in a map(in a range).
I have known another solution:
int main() {
map<int,int> m;
auto it=m.lower_bound(10);
cout<<it->first<<" "<<it->second<<endl;
return 0;
}
BUT, I want to how to use std::lower_bound(m.begin(),m.end(),***).
int main() {
map<int,int> m;
auto it=std::lower_bound(m.begin(),m.end(),10);
cout<<it->first<<" "<<it->second<<endl;
return 0;
}
main.cpp:29:43: required from here
/usr/local/Cellar/gcc/7.3.0_1/include/c++/7.3.0/bits/predefined_ops.h:65:22: error: no match for 'operator<' (operand types are 'std::pair' and 'const int')
{ return *__it < __val; }
The value_type of a map is std::pair<const Key,Value>, so you'll need to supply such a pair as argument.
Given that you are interested only in the key part, it's better to use the overload of std::lower_bound() that accepts a function object:
auto const it = std::lower_bound(m.begin(), m.end(), std::make_pair(10, 0),
[](auto const& a, auto const& b){ return a.first < b.first; });
I believe, from reading the docs, but haven't confirmed, that we can use the map's comparer:
auto const it = std::lower_bound(m.begin(), m.end(), std::make_pair(10, 0),
m.value_comp());
It seems you mean the following
#include <iostream>
#include <map>
#include <iterator>
#include <algorithm>
int main()
{
std::map<int, int> m =
{
{ 2, 1 }, { 4, 2 }, { 6, 3 }, { 8, 4 }, { 10, -1 }, { 10, 0 }, { 12, 2 }
};
int key = 10;
auto it = m.lower_bound( key );
std::cout << "{ " << it->first << ", " << it->second << " }\n";
it = std::lower_bound( std::begin( m ), std::end( m ), key,
[&]( const auto &p, const auto &value ) { return p.first < value; } );
std::cout << "{ " << it->first << ", " << it->second << " }\n";
return 0;
}
The program output is
{ 10, -1 }
{ 10, -1 }
That is in the standard algorithm std::lower_bound you can use a lambda expression.

How to determine size from (nested) std::initializer_list?

New to C++ and trying to wrap my head around initializer_list.
I'm making a Matrix class that effectively stores a 2d array of double values. I don't get the project on a structural level. Like okay we make a Matrix class that essentially stores a 2D array of data. But it needs to be able to store any size array, so it must use a dynamically allocated array. But std::array isn't allowed.
I have no idea how to access the items in the i_list. If they're passed in like
Matrix a = {{1, 2}, {3, 4}};
then according to the documentation I've seen, my only options for interaction with that information in the constructor are list.begin() which either points to the {1, 2} and list.end() which points to the {3,4}
std::vector and std::array are prohibited by the project description, and non-dynamic arrays obviously can't take in variables for size.
So how do I make this able to read a matrix of any size, and how do I take those values from my i_list and store them into something nondynamic?
I'm envisioning something like
Matrix::Matrix(const initializer_list & list) {
double * mat[/*somehow find out size without dynamic allocation*/];
for (double* i : mat) {
*i = list[i]; //not how i_list works apparently
}
}
Project description says:
You MAY NOT use library classes such as std::array, std::vector, std::list, etc. for this project. You must implement your Matrix class internally using a dynamically allocated array
initializer_lists are very cheap containers of [references to] temporary objects.
You can iterate over them as if they were arrays. In addition they also have a size() member so you can query their size.
Here is an example of passing a '2d' initializer_list to a function (which could easily be an constructor):
#include <initializer_list>
#include <iostream>
using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;
void info(list_of_list_of_doubles lld)
{
std::cout << "{\n";
for (auto& ld : lld) {
std::cout << " {";
auto sep = " ";
for (auto& d : ld) {
std::cout << sep << d;
sep = ", ";
}
std::cout << " }\n";
}
std::cout << "}\n";
}
int main()
{
info({
{ 1,2,3 },
{ 4.0, 5.0, 6.0 }
});
}
expected output:
{
{ 1, 2, 3 }
{ 4, 5, 6 }
}
Printing out the contents of the list is pretty simple, but what if I want to save them non-dynamically? I'm making a class constructor, and I want to have access to that data.
OK, so the requirement is that the storage in the class is non-dynamic (i.e. a fixed size).
I am going to make some assumptions:
let's say that the target class is a 3x3 matrix
any non-specified items in the initializer_list should be assumed to be zero.
passing in more than 3 rows or columns is a logic error and should cause an exception to be raised
Here's one (of many) ways:
#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <algorithm>
using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;
struct matrix
{
matrix(list_of_list_of_doubles lld)
: _storage {}
{
if (lld.size() > 3)
throw std::invalid_argument("too many rows");
auto row_idx = std::size_t { 0 };
for (auto& row : lld) {
if (row.size() > 3)
throw std::invalid_argument("too many columns");
std::copy(std::begin(row), std::end(row), std::begin(_storage[row_idx]));
++row_idx;
}
}
double _storage[3][3];
};
std::ostream& operator<<(std::ostream& os, const matrix& m)
{
std::cout << "{\n";
for (auto& ld : m._storage) {
std::cout << " {";
auto sep = " ";
for (auto& d : ld) {
std::cout << sep << d;
sep = ", ";
}
std::cout << " }\n";
}
return std::cout << "}";
}
int main()
{
matrix m({
{ 1,2,3 },
{ 4.1, 5.2, 6.3 },
{ 2.01, 4.5 } // ,0
});
std::cout << m << std::endl;
}
but I wanted a dynamically-sized 2-d array...
Oh go on then...
#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>
using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;
std::size_t total_extent(const list_of_list_of_doubles& lld)
{
return std::accumulate(std::begin(lld), std::end(lld), std::size_t(0),
[](auto tot, auto& container) {
return tot + container.size();
});
}
struct matrix
{
using value_storage = std::unique_ptr<double[]>;
using index_storage = std::unique_ptr<std::size_t>;
matrix(list_of_list_of_doubles lld)
: _total_extent { total_extent(lld) }
, _rows { lld.size() }
, _indecies { new std::size_t[_rows] }
, _storage { new double [_total_extent] }
{
auto istorage = _storage.get();
auto iindex = _indecies.get();
for (auto& row : lld) {
*iindex++ = istorage - _storage.get();
istorage = std::copy(std::begin(row), std::end(row), istorage);
}
}
std::size_t rows() const {
return _rows;
}
const double* column(std::size_t row) const {
return std::addressof(_storage[_indecies[row]]);
}
std::size_t column_size(std::size_t row) const {
return row == _rows - 1
? _total_extent - _indecies[row]
: _indecies[row + 1] - _indecies[row];
}
std::size_t _total_extent, _rows;
std::unique_ptr<std::size_t[]> _indecies;
std::unique_ptr<double[]> _storage;
};
std::ostream& operator<<(std::ostream& os, const matrix& m)
{
std::cout << "{\n";
for (std::size_t row = 0 ; row < m.rows() ; ++row) {
std::cout << " {";
auto sep = " ";
for (std::size_t col = 0 ; col < m.column_size(row) ; ++col) {
std::cout << sep << m.column(row)[col];
sep = ", ";
}
std::cout << " }\n";
}
return std::cout << "}";
}
int main()
{
matrix m({
{ 1,2,3 },
{ 4.1, 5.2, 6.3 },
{ 2.01, 4.5 } // ,0
});
std::cout << m << std::endl;
}
Perhaps, you are looking for something like this:
struct Matrix {
Matrix(std::initializer_list<std::initializer_list<double>> m) {
int max=0;
for (auto l: m)
if (m.size()>max)
max= m.size();
std::cout << "your matriz seems to be: "
<< m.size() << ' ' << max << std::endl;
}
};

how to access vector of map of ( int and vector of strings )

how do i access map of int and vectors of string in the passed_vector function.
I just want to print them in that function.
#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;
typedef vector< map< int, vector<string> > > vmis;
typedef map< int, vector<string> > mis;
typedef vector<string> vstr;
void passing_vector(const vmis &meetings);
//return size of vector
template< typename A > size_t n_elements( const A& a )
{
return sizeof a / sizeof a[ 0 ];
}
int main()
{
vmis meeting_info;
mis meeting_members;
vstr sw_vec;
vstr sys_vec;
string sw_team[] = {"Ricky", "John", "David"};
string sys_team[] = {"Simmon", "Brad", "Schmidt", "Fizio"};
sw_vec.insert(sw_vec.begin(), sw_team, sw_team + n_elements(sw_team) );
sys_vec.insert(sys_vec.begin(), sys_team, sys_team + n_elements(sys_team) );
meeting_members.insert(make_pair(520, sw_vec));
meeting_members.insert(make_pair(440, sys_vec));
meeting_info.push_back(meeting_members);
passing_vector(meeting_info);
return 0;
}
void passing_vector(const vmis &meetings)
{
vmis::iterator itvmis = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
}
I know how to print them in main function.
vmis::iterator itvims = meeting_info.begin();
for( int i = 0; i < meeting_info.size(); i++ )
{
mis::iterator itm = meeting_members.begin();
for(itm; itm != meeting_members.end(); itm++ )
{
cout << itm->first << " : ";
vstr::iterator it = itm->second.begin();
for(it; it != itm->second.end(); it++)
cout << *it << " ";
cout << endl;
}
}
desired output
440 : Simmon Brad Schmidt Fizio
520 : Ricky John David
if there is a better way of doing this suggestions are always welcome.
The easiest aproach is to use auto, also since your meetings is const, you need to use const_iterator:
void passing_vector(const vmis &meetings)
{
vmis::const_iterator itvims = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
for (;itvims != meetings.end(); ++itvims)
{
const auto& map_item = *itvims;
for (const auto& map_it : map_item)
{
int map_key = map_it.first;
const auto& str_vec = map_it.second;
for (const auto& str : str_vec)
{
std::cout << map_key << " - " << str << "\n";
}
}
}
}
[edit]
c++98 version:
void passing_vector(const vmis &meetings)
{
vmis::const_iterator itvims = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
for (;itvims != meetings.end(); ++itvims)
{
const mis& map_item = *itvims;
for (mis::const_iterator map_it = map_item.begin(); map_it != map_item.end(); ++map_it)
{
int map_key = map_it->first;
const vstr& str_vec = map_it->second;
for (vstr::const_iterator sitr = str_vec.begin(); sitr != str_vec.end(); ++sitr)
{
std::cout << map_key << " - " << *sitr << "\n";
}
}
}
}

C++ how can I implement a hash set from a hash table using a template class child of a template class?

I have a school assignment to implement a hash_set and a hash_map, I am given a main.cpp that has code to test these classes (inserts, deletes, searches, prints, etc). I am also given a base hash_table class that is a templated class, and just told to "make the hash_set and hash_map work based on the testing code with new classes of their own".
Without more specific details, I assume I am supposed to implement the set and map based on the hash_table, which the teacher said the project could be completed in 40 lines of code (wat??) between the two new classes...but I can't even get the hash_set class to inherit the hash_table since it's a template. I can get it compiling ok without making the hash_set class a template, until I instantiate it, since the testing code is passing it a template type in the declaration. But if I try to make the class a template type itself to compensate for this, everything breaks and I can't track down the source of the error. I'm about 8 hours into this project which should supposedly be a 1 hour project, so I'm at wits end here.
Here is the original main.cpp with the testing code (hash_map is commented out because I haven't even started that yet :/ )
//
// main.cpp
// CS3100Project05_HashCollections
#include "WSUHashTable.h"
//#include "WSUHashMap.h" // Uncomment to test WSUHashMap
#include "WSUHashSet.h" // Uncomment to test WSUHashSet
#include <iostream>
int main(int argc, const char * argv[])
{
///////////////////////////////////////////////////////////////////
/// Part 1 :TEST HASH TABLE ///
///////////////////////////////////////////////////////////////////
{
WSUHashTable<int> example = {1, 2, 3, 4, 1};
std::cout << "Part 1a: Expected output is in any order " <<
"\"1 2 3 4\"\n";
for(int n : example)
{
std::cout << n << ' ';
}
std::cout << std::endl;
std::cout << std::endl;
std::cout << "Part 1b: Expected output is \"Found 2\"\n";
{
auto search = example.find(2);
if(search != example.end())
{
std::cout << "Found " << (*search) << '\n';
}
else
{
std::cout << "Not found\n";
}
}
std::cout << std::endl;
std::cout << "Part 1c: Expected output is \"Not found\"\n";
example.erase(2);
{
auto search = example.find(2);
if(search != example.end()) {
std::cout << "Found " << (*search) << '\n';
}
else {
std::cout << "Not found\n";
}
}
std::cout << std::endl;
}
///////////////////////////////////////////////////////////////////
/// Part 2: TEST HASH SET ///
///////////////////////////////////////////////////////////////////
/***** Uncomment to test WSUHashSet */
{
WSUHashSet<std::string> stringSet = {"1", "2", "3", "4"};
std::cout << "Part 2a: Expected output is \"Found \"2\"\"\n";
{ // Test find() that succeeds
auto search = stringSet.find("2");
if(search != stringSet.end()) {
std::cout << "Found \"" << (*search) << "\"\n";
}
else {
std::cout << "Not found\n";
}
}
std::cout << std::endl;
std::cout << "Part 2b: Expected output is \"Not found\"\n";
stringSet.erase("2");
{ // Test find() that fails
auto search = stringSet.find("2");
if(search != stringSet.end())
{
std::cout << "Found \"" << (*search) << "\"\n";
}
else
{
std::cout << "Not found\n";
}
}
std::cout << std::endl;
WSUHashSet<double> doubleSet = {
1.1, 2.2, 3.2, 4.4, 5.5, 6.1, 7.2, 8.4, 9.9
};
std::cout << "Part 2c: Expected output is in any order " <<
"\"5.5 7.2 8.4 9.9 1.1 2.2 3.2 4.4 6.1\"\n";
for(auto n : doubleSet )
{ // Test using implicit iterators
std::cout << n << ' ';
}
std::cout << std::endl;
std::cout << std::endl;
std::cout << "Part 2d: Expected output is in any order " <<
"\"5.5 7.2 8.4 9.9 4.4 6.1\"\n";
// Part 7: Using explicit iterators while mutating set
for(auto it = doubleSet.begin(); it != doubleSet.end(); )
{ // erase every number less than 4.0
if(*it < 4.0)
{
it = doubleSet.erase(it);
}
else
{
++it;
}
}
for(auto n : doubleSet)
{
std::cout << n << ' ';
}
std::cout << std::endl;
std::cout << std::endl;
}
///////////////////////////////////////////////////////////////////
/// Part 3: TEST HASH MAP ///
///////////////////////////////////////////////////////////////////
/***** Uncomment to test WSUHashMap *
{
WSUHashMap<int, std::string> dict = {{1, "one"}, {2, "two"}};
dict.insert({3, "three"});
dict.insert(std::make_pair(4, "four"));
dict.insert({{4, "another four"}, {5, "five"}});
std::cout << "Part 3a: Expected output is " <<
"\"inserting 1 -> \"another one\" failed\"\n";
bool ok = dict.insert({1, "another one"}).second;
std::cout << "inserting 1 -> \"another one\" "
<< (ok ? "succeeded" : "failed") << '\n';
std::cout << std::endl;
std::cout << "Part 3b: Expected output is " <<
"\"contents: " <<
"1 => one " <<
"2 => two " <<
"3 => three " <<
"4 => four " <<
"5 => five\"\n";
std::cout << "contents: ";
for(auto& p : dict)
{
std::cout << " " << p.first << " => " << p.second << ' ';
}
std::cout << std::endl;
}
*****/
return 0;
}
And this is the original hash_table class file:
//
// WSUHashTable.h
// CS3100Project05_HashCollections
#ifndef __CS3100Project05_HashCollections__WSUHashTable_H
#define __CS3100Project05_HashCollections__WSUHashTable_H
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include <cassert>
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
template <
typename elementT,
typename Hash = std::hash<elementT>
>
class WSUHashTable
{
private:
///////////////////////////////////////////////////////////////////
typedef elementT bucket_t;
///////////////////////////////////////////////////////////////////
static const std::size_t initialCapacity = (1 << 2);
constexpr static float maxLoadFactor = 0.73f;
///////////////////////////////////////////////////////////////////
std::size_t mSize; // Number of elements in table
std::vector<bucket_t> mStorage; // Storage for elements
std::vector<bool> mIsValidFlags;
public:
///////////////////////////////////////////////////////////////////
class iterator : public std::iterator<
std::input_iterator_tag, elementT>
{
friend class WSUHashTable<elementT, Hash>;
const WSUHashTable<elementT, Hash> *mHashTablePtr;
std::size_t mIndex;
////////////////////////////////////////////////////////////////
iterator(
const WSUHashTable<elementT, Hash> &hashTable,
std::size_t index = 0
) :
mHashTablePtr(&hashTable),
mIndex(index)
{
}
////////////////////////////////////////////////////////////////
std::size_t getIndex() const
{
return mIndex;
}
public:
////////////////////////////////////////////////////////////////
iterator(const iterator &other) :
mHashTablePtr(other.mHashTablePtr),
mIndex(other.mIndex)
{
}
////////////////////////////////////////////////////////////////
iterator& operator++()
{
++mIndex;
while(mIndex < mHashTablePtr->mIsValidFlags.size() &&
!mHashTablePtr->mIsValidFlags[mIndex])
{ // Skip over empty buckets
++mIndex;
}
return *this;
}
////////////////////////////////////////////////////////////////
iterator operator++(int)
{
const_iterator tmp(*this);
operator++();
return tmp;
}
////////////////////////////////////////////////////////////////
bool operator==(const iterator& rhs)
{
return mIndex == rhs.mIndex;
}
////////////////////////////////////////////////////////////////
bool operator!=(const iterator& rhs)
{
return mIndex != rhs.mIndex;
}
////////////////////////////////////////////////////////////////
const elementT &operator*()
{
return mHashTablePtr->mStorage[mIndex];
}
};
///////////////////////////////////////////////////////////////////
typedef const iterator const_iterator;
private:
typedef std::pair<const_iterator, bool> _findResult;
///////////////////////////////////////////////////////////////////
std::size_t _calculatedIndex(const elementT &element) const
{
return (Hash()(element) % mStorage.size());
}
///////////////////////////////////////////////////////////////////
// Returns a pair containing iterator to bucket where element
// should be and flag indicating whether it is there.
_findResult _find( const elementT &element ) const
{
std::size_t index = _calculatedIndex(element);
while(mIsValidFlags[index] &&
((mStorage[index] < element) || (element < mStorage[index])))
{ // Loop until element is found or an empty bucket is found
++index;
index %= mStorage.size();
}
return _findResult(
const_iterator(*this, index), mIsValidFlags[index]);
}
///////////////////////////////////////////////////////////////////
void _doubleCapacityAndRehash()
{
const std::size_t oldSize = mIsValidFlags.size();
// Save off mStorage by moving contents instead of copying
std::vector<bucket_t> oldStorage = std::move(mStorage);
std::vector<bool> oldIsValidFlags = std::move(mIsValidFlags);
// Replace mStorage and mIsValidFlags with empty storage with
// twice the size/capacity.
mStorage = std::move(std::vector<bucket_t>(oldSize * 2));
mIsValidFlags = std::move(std::vector<bool>(oldSize * 2));
// We are going to re-insert everything, so strat with size 0
mSize = 0;
for(std::size_t i = 0; i < oldSize; ++i)
{ // Insert values from all valid buckets in old storage
if(oldIsValidFlags[i])
{
insert(oldStorage[i]);
}
}
}
public:
///////////////////////////////////////////////////////////////////
WSUHashTable() :
mSize(0),
mStorage(initialCapacity),
mIsValidFlags(initialCapacity)
{
}
///////////////////////////////////////////////////////////////////
WSUHashTable(const WSUHashTable &other) :
mSize(other.mSize),
mStorage(other.mStorage),
mIsValidFlags(other.mIsValidFlags)
{
}
///////////////////////////////////////////////////////////////////
template< class InputIt >
WSUHashTable(InputIt first, InputIt last) :
mSize(0),
mStorage(initialCapacity),
mIsValidFlags(initialCapacity)
{
while(first != last)
{
insert(*first);
++first;
}
}
///////////////////////////////////////////////////////////////////
WSUHashTable(std::initializer_list<elementT> init) :
mSize(0),
mStorage(initialCapacity),
mIsValidFlags(initialCapacity)
{
insert(init);
}
///////////////////////////////////////////////////////////////////
std::size_t size() const
{
return mSize;
}
///////////////////////////////////////////////////////////////////
/// Inserts element(s) into the container, if the container doesn't
/// already contain an an equivalent element.
/// Returns a pair consisting of an iterator to the inserted
/// element (or to the element that prevented the insertion) and a
/// bool denoting whether the insertion took place.
std::pair<const_iterator, bool> insert( const elementT &element )
{
if(mSize > (maxLoadFactor * mStorage.size()))
{ // resize to make room for insertion
_doubleCapacityAndRehash();
}
_findResult result = _find(element);
if(result.second)
{ // element is present
return std::pair<const_iterator, bool>(result.first, false);
}
const std::size_t index = result.first.getIndex();
mStorage[index] = element;
mIsValidFlags[index] = true;
++mSize;
return std::pair<const_iterator, bool>(result.first, true);
}
///////////////////////////////////////////////////////////////////
/// Inserts element(s) into the container, if the container doesn't
/// already contain an an equivalent element.
/// Returns a pair consisting of an iterator to the inserted
/// element (or to the element that prevented the insertion) and a
/// bool denoting whether the insertion took place.
///
/// An && argumnet signals an "emplace" operation in C++11. The
/// value will be moved via std::move() instead of copied.
std::pair<const_iterator, bool> insert( elementT &&element )
{
if(mSize > (maxLoadFactor * mStorage.size()))
{ // resize to make room for insertion
_doubleCapacityAndRehash();
}
_findResult result = _find(element);
if(result.second)
{ // element is present
return std::pair<const_iterator, bool>(
result.first, false);
}
const std::size_t index = result.first.getIndex();
mStorage[index] = std::move(element);
mIsValidFlags[index] = true;
++mSize;
return std::pair<const_iterator, bool>(result.first, true);
}
///////////////////////////////////////////////////////////////////
void insert( std::initializer_list<elementT> ilist )
{
for(const elementT &element : ilist)
{
insert(element);
}
}
///////////////////////////////////////////////////////////////////
/// Returns iterator following the last removed element.
const_iterator erase( const_iterator pos )
{
const std::size_t index = pos.getIndex();
if(mIsValidFlags[index])
{ // element is present
mIsValidFlags[index] = false;
--mSize;
}
return ++iterator(pos);
}
///////////////////////////////////////////////////////////////////
// Returns the number of elements erased (it will be zero or one).
std::size_t erase(const elementT &element)
{
_findResult result = _find(element);
if(result.second)
{ // element is present
mIsValidFlags[result.first.getIndex()] = false;
--mSize;
return 1;
}
return 0;
}
///////////////////////////////////////////////////////////////////
const_iterator find( const elementT &element ) const
{
_findResult result = _find(element);
if(result.second)
{ // element was found
return result.first;
}
return end();
}
///////////////////////////////////////////////////////////////////
const_iterator begin() const
{
std::size_t index = 0;
while(index < mIsValidFlags.size() && !mIsValidFlags[index])
{ // Skip over empty buckets
++index;
}
return const_iterator(*this, index);
}
///////////////////////////////////////////////////////////////////
const_iterator end() const
{
return const_iterator(*this, mStorage.size());
}
};
#endif /* defined(__CS3100Project05_HashCollections__WSUHashTable_H) */
I know the hash_table file is long, and I have a decent idea on how to proceed, since a hash_set really only needs to implement a search function within the insert to make sure the list is unique, but doesn't really mess with hash order or anything (I'm not really sure how to implement the hash itself, but I assume the inheritance will sort that out nicely), and the hash_map will allow a null key and any null values....but first something has to compile.
This is what I have currently as my hash_set class, just trying to get a simple constructor that uses the parent class's constructor with the correct type (std::string):
#ifndef __WSUHashSet__
#define __WSUHashSet__
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include "WSUHashTable.h"
/*template<
typename elementT,
typename Hash = std::hash<elementT>
>*/
class WSUHashSet : public WSUHashTable<std::string> {
private:
public:
WSUHashSet(std::initializer_list<std::string> init) : WSUHashTable(init)
{
insert(init);
}
};
#endif //__WSUHashSet__
Currently I get the same error: with this exact code it tells me that "WSUHashSet is not a template", which is good because my class is fine, but bad because I can't just edit the main.cpp to not treat it as a template.
When I try to make it a template, like this:
#ifndef __WSUHashSet__
#define __WSUHashSet__
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include "WSUHashTable.h"
template<
typename elementT,
typename Hash = std::hash<elementT>
>
class WSUHashSet<elementT> : public WSUHashTable<std::string> {
private:
public:
WSUHashSet<elementT>::WSUHashSet(std::initializer_list<std::string> init) : WSUHashTable(init)
{
insert(init);
}
};
#endif //__WSUHashSet__
I really need some direction here. I can't afford to spend a lot more time on this as it's not my only class, and I feel like this should be fairly simple. The theory makes sense, but the implementation makes me feel like I'm blindly wandering in circles.
Thanks
EDIT: the actual compliler error is
WSUHashSet.h line 19: error: WSUHashSet is not a template
Apparently the pasting into the code block here caused confusion. Here's the actual line (19 in codeblocks) that is breaking the compilation:
WSUHashSet(std::initializer_list<std::string> init) : WSUHashTable(init)
{
insert(init);
}
If you look at the test code, you can see the the types you create must be templates, as they're instantiated for specific element types:
WSUHashTable<int> example = {1, 2, 3, 4, 1};
WSUHashSet<double> doubleSet = { ...
You show your attempt to make a template:
template<
typename elementT,
typename Hash = std::hash<elementT>
>
class WSUHashSet<elementT> : public WSUHashTable<std::string> {
public:
WSUHashSet<elementT>::WSUHashSet(std::initializer_list<std::string> init) : WSUHashTable(init)
{
insert(init);
}
...
That's not so far off... try:
template <typename elementT>
class WSUHashSet<elementT> : public WSUHashTable<elementT>
{
public:
WSUHashSet(std::initializer_list<std::string> init)
: WSUHashTable<elementT>(init)
{ }