vector of lists + iterator CPP - c++

I am trying to implement insertion of a word into a chained hashtable.
The problem is I am trying to insert a object that has 2 fileds and I need access to one with an iterator. The problem seems to happen with the iterator it as the code doesn't work from the for cycle. I also overloaded the operator== in Vocabolo.cpp to make it work for my case.
I also have a problem the size of the vector, can I use a define? It seems not. Any advices please?
I declared my vector of list + iterator in the header file as :
vector<list<Vocabolo>> hash;
list<Vocabolo>::iterator it;
this is part of the class Vocabolo :
class Vocabolo {
public:
Vocabolo();
~Vocabolo();
void setVocabolo(Vocabolo);
string getVocabolo();
bool operator== (Vocabolo);
string termine;
string tipo;
};
this is the overloaded method operator==:
bool Vocabolo::operator== (Vocabolo x) {
return getVocabolo() == x.termine;
}
the method that is not working!
bool HashV::Insert(Vocabolo nuovo) {
key = this->HashUniversale(nuovo.termine);
for (it = this->hash[key].begin(); it != this->hash[key].end(); it++)
if (it->termine == nuovo.termine)
return false;
else {
hash[key].push_back(nuovo);
return true;
}
}

Consider using std::find_if instead:
auto itVoca = std::find_if(this->hash[key].begin(), this->hash[key].end(), [nuovo](const string& str)
{
return str != nuovo.termine;
});
bool found = itVoca != this->hash[key].end();
if(found ) hash[key].push_back(nuovo);
return found;

Related

C++ - Overloading of operators needed for an iterator

I'm trying to create an iterator on a library that allows reading a specific file format.
From the docs, to read the file content you need do something like this:
CKMCFile database;
if (!database.OpenForListing(path)) {
std::cerr << "ERROR: unable to open " << path << std::endl;
}
CKMCFileInfo info;
database.Info(info);
CKmerAPI kmer(info.kmer_length);
uint32 cnt;
std::vector<uint64_t> data;
std::vector<uint64> ulong_kmer;
data.reserve(info.total_kmers);
while (database.ReadNextKmer(kmer, cnt)) {
kmer.to_long(ulong_kmer);
data.push_back(ulong_kmer[0]);
}
Now, I started with this class wrapper:
class FileWrapper {
CKMCFile database;
CKMCFileInfo info;
Iterator _end;
public:
explicit FileWrapper(const std::string &path) {
if (!database.OpenForListing(path)) {
std::cout << "ERROR: unable to open " << path << std::endl;
}
database.Info(info);
}
Iterator begin() {
Iterator it;
it.database = &database;
it.total = 0;
uint32_t cnt;
std::vector<uint64_t> ulong_kmer;
CKmerAPI tmp(info.kmer_length);
database.ReadNextKmer(tmp, cnt);
tmp.to_long(ulong_kmer);
return it;
}
Iterator end() const { return _end; }
uint64_t size() { return info.total_kmers; }
};
And then, this is the Iterator class:
class Iterator {
friend class FileWrapper;
CKMCFileInfo info;
CKMCFile *database;
uint64_t kmer, total;
public:
Iterator &operator++() {
++total;
uint32_t cnt;
std::vector<uint64_t> ulong_kmer;
CKmerAPI tmp(info.kmer_length);
database->ReadNextKmer(tmp, cnt);
tmp.to_long(ulong_kmer);
return *this;
}
bool operator<(const Iterator &rhs) const { return total < rhs.total; }
uint64_t operator*() const { return kmer; }
};
But, during some test I can't use into a for loop for something like for (auto it = begin(); it != end(); ++i) { ... } or begin() + size(). How can I overload correctly this two operatos? opeartor!= and operato+
You'll have to think about 2 major things before:
Ownership. Currently, you have to make sure your FileWrapper survives at least as long as any Iterator returned from it by calling its begin() (since your Iterators store pointers to data owned by the FileWrapper object). If you cannot guarantee that, maybe think about using unique_ptrs or shared_ptrs
Iterator Category. As discussed in the comments, it appears that your database requires you to use "input iterators". They can only be incremented by one (do not provide operator+(int)) and dereferenced. Indeed, what would the iterator begin() + 10 look like? If this should advance your file-pointer, then you cannot define the end as begin() + size() as that would just skip through the file.
Representation. What should an end-iterator look like? A simple choice might be to indicate the end with database == nullptr. In this case, an operator!= might look like this:
bool is_end() const { return database == nullptr; }
bool operator==(const Iterator& other) const {
if(is_end()) return other.is_end();
if(other.is_end()) return false;
return (database == other.database) && (total == other.total);
}
bool operator!=(const Iterator& other) const { return !operator==(other); }
Now, you'll need code that ensures that all end-iterators have database == nullptr and, whenever a non-end iterator becomes and end-iterator by application of operator++(), you'll need to set database = nullptr and total = 0 (or something).
A note at the end: your Iterators may be in an inconsistent state after construction and before assignment of their database member. It is prudent to declare a proper constructor for Iterator that initializes its members.
EDIT: here's a suggestion for an integration

How can I get around from using "[]" operator on const unordered_map in C++?

I have this function below with wordcount_map being an std::unordered_map <std::string, int> type. I'm using the count function to see if the key is in the map and if it is to return the value stored at that key. However, I'm getting an error because map is marked as const and [] operator is not allowed. Please advise!
int lookupWithFallback(const StringIntMap& wordcount_map, const std::string& key, int fallbackVal) {
if (wordcount_map.count(key)){
return wordcount_map[key];
}
else {
return fallbackVal;
}
}
Use the method "find" which is const :
https://en.cppreference.com/w/cpp/container/unordered_map/find
In your case, something like :
int lookupWithFallback(const StringIntMap& wordcount_map, const std::string& key, int fallbackVal) {
auto it = wordcount_map.find(key);
if(it != wordcount_map.end())
return it->second;
else
return fallbackVal;
}
The operator [] is not const because if the key doesn't exist, it will be created.
https://en.cppreference.com/w/cpp/container/unordered_map/operator_at
Use the find member function:
int lookupWithFallback(const StringIntMap& wordcount_map,
const std::string& key,
int fallbackVal)
{
auto it = wordcount_map.find(key);
return it != wordcount_map.end() ? it->second : fallbackVal;
}
This method will also only do one lookup, and can be useful even when you do want to modify the map.
Even if your map were non-const, your algorithm requires a redundant lookup.
You can either use std::unordered_map::find as suggested in other answers:
int lookupWithFallback(const StringIntMap& wordcount_map, const std::string& key, int fallbackVal) {
if (auto const it = wordcount_map.find(key); it != wordcount_map.end()) {
return it->second;
} else {
return fallbackVal;
}
}
or you can use std::unordered_map::at and catch an exception instead of passing a fallbackVal:
try {
return wordcount_map.at(key);
} catch (std::out_of_range const& oor) {
/* handle error in a sensible way or: */
return fallbackVal;
}
Passing default values is a problem for types more complicated than int so you may want to consider handling a non-existent value as an error. However, exceptions should not be used for expected cases. This depends on your setting.

C++ custom lazy iterator

I have a somewhat simple text file parser. The text I parse is split into blocks denoted by { block data }.
My parser has a string read() function, which gets tokens back, such that in the example above the first token is { followed by block followed by data followed by }.
To make things less repetitive, I want to write a generator-like iterator that will allow me to write something similar to this JavaScript code:
* readBlock() {
this.read(); // {
let token = this.read();
while (token !== '}') {
yield token;
token = this.read();
}
}
which in turn allows me to use simple for-of syntax:
for (let token of parser.readBlock()) {
// block
// data
}
For C++ I would like something similar:
for (string token : reader.read_block())
{
// block
// data
}
I googled around to see if this can be done with an iterator, but I couldn't figure if I can have a lazy iterator like this which has no defined beginning or end. That is, its beginning is the current position of the reader (an integer offset into a vector of characters), and its end is when the token } is found.
I don't need to construct arbitrary iterators, or to iterate in reverse, or to see if two iterators are equal, since it's purely to make linear iteration less repetitive.
Currently every time I want to read a block, I need to re-write the following:
stream.skip(); // {
while ((token = stream.read()) != "}")
{
// block
// data
}
This becomes very messy, especially when I have blocks inside blocks. To support blocks inside blocks, the iterators would have to all reference the same reader's offset, such that an inner block will advance the offset, and the outer block will re-start iterating (after the inner is finished) from that advanced offset.
Is this possible to achieve in C++?
In order to be usable in a for-range loop, a class has to have member functions begin() and end() which return iterators.
What is an iterator? Any object fulfilling a set of requirements. There are several kind of iterators, depending on which operations allow you. I suggest to implement an input iterator, which is the simplest: https://en.cppreference.com/w/cpp/named_req/InputIterator
class Stream
{
public:
std::string read() { /**/ }
bool valid() const { /* return true while more tokens are available */ }
};
class FileParser
{
std::string current_;
Stream* stream_;
public:
class iterator
{
FileParser* obj_;
public:
using value_type = std::string;
using reference = const std::string&;
using pointer = const std::string*;
using iterator_category = std::input_iterator_tag;
iterator(FileParser* obj=nullptr): obj_ {obj} {}
reference operator*() const { return obj_->current_; }
iterator& operator++() { increment(); return *this; }
iterator operator++(int) { increment(); return *this; }
bool operator==(iterator rhs) const { return obj_ == rhs.obj_; }
bool operator!=(iterator rhs) const { return !(rhs==*this); }
protected:
void increment()
{
obj_->next();
if (!obj_->valid())
obj_ = nullptr;
}
};
FileParser(Stream& stream): stream_ {&stream} {};
iterator begin() { return iterator{this}; }
iterator end() { return iterator{}; }
void next() { current_ = stream_->read(); }
bool valid() const { return stream_->valid(); }
};
So your end-of-file iterator is represented by an iterator pointing to no object.
Then you can use it like this:
int main()
{
Stream s; // Initialize it as needed
FileParser parser {s};
for (const std::string& token: parser)
{
std::cout << token << std::endl;
}
}

finding an element in a multimap ONLY with its value c++

First of all sorry if I am asking stupid questions, but I am a beginer in c++.
I am writing a system that represents a library and there is a member function of my Library class that is supposed to allow us to remove a book. Now, if the book is loaned by a user, means there is an element in my _usersLoaningMultimap (multimap<UserId,LoanInfo>). How can I find the LoanInfo that I want without knowing the key (UserId)?
bool Library::removeBook(const BookId& bookId){
//how to find my book in my library without knowing who loaned it.
}
Just to make it clearer, my class Library is like that:
class Library {
public:
Library();
void addUser(const UserId&, const string&);
Optional<string>& getUserInfo(const UserId& userId);
void addBook(const BookId& bookId, const string& description);
Optional<string>& getBookInfo(const BookId& bookId);
bool returnBook(const UserId& userId, const BookId& bookId);
void loanBook(const UserId& userId,LoanInfo& loan);
bool removeUser(const UserId& userId);
void getLoansSortedByDate(const UserId,std::vector<LoanInfo>& loanVector);
~Library() {}
private:
map<BookId, string> _bookMap;
map<UserId, string> _userMap;
multimap<UserId, LoanInfo> _usersLoaningMultimap;
};
You have to iterate through the whole map like this :
for(multimap<userId,LoanInfo>::iterator it = _usersLoaningMultimap.begin(); it != _usersLoaningMultimap.end(); it++){
//it->first retrieves key and it->second retrieves value
if(it->second == loan_info_you_are_searching){
//do whatever
}
}
std::multimap doesn't provide any method for value lookup. Your only choice is to read through the multimap looking for a particular value.
You can use std::find_if for that purpose :
using const_ref = std::multimap<UserId, LoanInfo>::const_reference;
std::find_if(_usersLoaningMultimap.begin(), _usersLoaningMultimap.end(),
[&](const_ref a) -> bool {
return a.second == your_loan_info;
});
If you don't like the syntax, you can also make your own function :
using Map = std::multimap<UserId, LoanInfo>;
auto findLoanInfo(const Map& map, const LoanInfo& info) -> Map::iterator {
for (auto it = map.begin(); it != map.end(); ++it) {
if (it->second == info) {
return it;
}
}
return map.end();
}

returning c++ iterators

I have a function that returns an iterator if an object is found.
Now i have a problem. How do i fix the problem of informing the object that called this function that the object was not found?
vector<obj>::iterator Find(int id, int test)
{
vector<obj>::iterator it;
aClass class;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
return it;
}
return ???? // <<< if not found what to insert here?
}
Do i need to change my data structure in this instead?
Thanks in advance! :)
Return vector::end(), throw an exception, or return something other than a plain iterator
Better yet, don't implement your own Find function. That is what the <algorithm> library is for. Based on your psudocode, you can probably use std::find or std::find_if. find_if is particularly useful in cases where equality doesn't necessarily mean operator==. In those cases, you can use a [C++11] lambda or if C++11 isn't available to you, a functor class.
Since the functor is the lowest common denominator, I'll start with that:
#include <cstdlib>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;
class Person
{
public:
Person(const string& name, unsigned age) : name_(name), age_(age) {};
string name_;
unsigned age_;
};
class match_name : public unary_function <bool, string>
{
public:
match_name(const string& rhs) : name_(rhs) {};
bool operator()(const Person& rhs) const
{
return rhs.name_ == name_;
}
private:
string name_;
};
#include <iostream>
int main()
{
vector<Person> people;
people.push_back(Person("Hellen Keller", 99));
people.push_back(Person("John Doe", 42));
/** C++03 **/
vector<Person>::const_iterator found_person = std::find_if( people.begin(), people.end(), match_name("John Doe"));
if( found_person == people.end() )
cout << "Not FOund";
else
cout << found_person->name_ << " is " << found_person->age_;
}
found_person now points to the person whose name is "John Doe", or else points to people_.end() if that person wasn't found.
A C++11 lambda is new language syntax that makes this process of declaring/defining a functor and using is somewhat simpler for many cases. It's done like this:
string target = "John Doe";
vector<Person>::const_iterator found_person = std::find_if(people.begin(), people.end(), [&target](const Person& test) { return it->name_ == target; });
You can return an iterator to the end, i.e. return class.vecCont.end() to indicate that.
How about just returning the end iterator?
Your code becomes:-
vector<obj>::iterator Find(int id, int test)
{
vector<obj>::iterator it;
aClass class;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
break;
}
return it;
}
or just use std::find.
You should return class.vecCont.end() if the object was not found. But #chris is right - this is exactly what std::find is for.
Something like this
std::vector<obj>::iterator pos;
pos = find(coll.begin(),coll.end(), val);
And don't forget to these check for presence of your element or not in the container
if (pos != coll.end())
Don't return an iterator to a hidden container. Return simply what it is that you want, namely a means to access an object if it exists. In this example, I store the objects in the container via pointer. If your objects only exist temporarily, then new one up and copy the object over!
class AClass;
//...some time later
std::vector<AClass*> vecCont; //notice, store pointers in this example!
//..some time later
AClass * findAClass(int id, int test)
{
vector<AClass*>::iterator it;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
return it;
}
return NULL;
}
//later still..
AClass *foundVal = findAClass(1, 0);
if(foundVal)
{
//we found it!
}
else
{
//we didn't find it
}
edit: the intelligent thing to do is to write a comparator for your class and use the std algorithms sort and to find them for you. However, do what you want.
Never emulate std::algorithm functions inside a class. They are free functions for a reason. It usually is enough to expose begin and end member function that return the right iterators (and possibly a boost::iterator_range). If you need to do a fancy find with a functor, expose the functor as well.