Question: Is there a better way to loop through all the vectors in the structure than by calling them one by one as shown in the example? #
I am building a classifier. There are several categories of items each represented by a vector of string. Each of the vectors will be fairly small, < 100 elements. I have read the examples in the following link:
How to find if an item is present in a std::vector?
Here is a simplified example of the code that I am implementing. The code compiles and runs but seems clunky to me. Thanks in advance.
#include "stdafx.h"
#include <vector>
#include <string>
#include <iostream>
using namespace std;
struct Categories
{
vector <string> cars;
vector <string> food;
};
struct ThingsType
{
Categories iLike;
Categories dontLike;
};
typedef vector<string>::const_iterator vIter;
int FindValue(vector<string>& vec, string keyWord)
{
int indx = -1;
vIter iter = find(vec.begin(), vec.end(), keyWord);
if (iter != vec.end()){
// found it
std::cout << "Value: " << *iter << " found in location: " << iter - vec.begin() << endl;
indx = iter - vec.begin();
}
else
{
// did not find it.
std::cout << "Value: " << keyWord << " not found." << endl;
}
return indx;
}
int _tmain(int argc, _TCHAR* argv[])
{
int result[10];
ThingsType things;
things.iLike.cars = { "Mustang", "Pinto" };
things.dontLike.food = { "Squash" };
string item("Pinto");
// I want to loop over all vectors searching for items
result[0] = FindValue(things.iLike.cars, item);
result[1] = FindValue(things.iLike.food, item);
// . . .
result[9] = FindValue(things.dontLike.food, item);
return 0;
}
You can keep the struct, and store pointers to each struct member in a list or vector.
struct Categories
{
vector <string> cars;
vector <string> food;
};
std::vector<std::vector<string>*> members;
Categories cat;
members.push_back(&cat.cars); // add a pointer to the cars struct member
members.push_back(&cat.food); // add a pointer to the food struct member
Now you can iterate via members, or access via the struct.
for(std::vector<string>* member: members)
{
for(string s: *member)
{
}
}
Or you could just forego the structure altogether and use the vector of vectors, that way you can iterate the "members" while still allowing O(1) access time for each member.
const int CARS = 0;
const int FOOD = 1;
members[CARS].push_back(car); // add a car
members[FOOD].push_back(food); // add food
// iterate cars
for(string car: members[CARS])
{
// do something
}
Try using a std::map
using namespace std;
typedef set<string> CategoryType;
struct ThingsType
{
map<string, CategoryType> iLike;
map<string, CategoryType> dontLike;
};
int _tmain(int argc, _TCHAR* argv[])
{
ThingsType things;
things.iLike['cars'].insert("Mustang");
things.iLike['cars'].insert("Pinto");
things.iLike['food'].insert("Squash");
string item("Pinto");
// I want to loop over all vectors searching for items
for(map<string, CategoryType>::const_iterator i = things.iLike.begin(); i != things.iLike.end(); i++) {
if (i->second.find(item) != set::end)
cout << "I like " << item << endl;
}
for(map<string, CategoryType>::const_iterator i = things.dontLike.begin(); i != things.dontLike.end(); i++) {
if (i->second.find(item) != set::end)
cout << "I don't like " << item << endl;
}
return 0;
}
I think std::unordered_set would be better in this case.
#include<unordered_set>
struct Categories{
std::unordered_set <std::string> cars;
std::unordered_set <std::string> food;
};
struct ThingsType{
Categories iLike;
Categories dontLike;
};
int main(){
std::unordered_set<std::string>::iterator result[10];
ThingsType things;
things.iLike.cars = { "Mustang", "Pinto" };
things.dontLike.food = { "Squash" };
string item("Pinto");
result[0] = things.iLike.cars(item);
result[1] = things.iLike.food(item);
// . . .
result[9] = things.dontLike.food(item);
}
Related
I am trying to achieve the creation of such a map. The following code attempts to do this
#include <list>
#include <map>
#include <string>
class IntWithString {
private:
int a;
std::string s;
public:
IntWithString(int a, std::string s) : a(a), s(s) {}
std::string getString() { return s; }
int getInt() { return a; }
};
namespace {
std::map<std::string, std::list<IntWithString *> > m;
}
void appendMap(IntWithString *a) {
auto it = m.find(a->getString());
if (it != m.end()) {
m[a->getString()].push_back(a);
} else {
std::list<IntWithString *> l;
l.push_back(a);
m[a->getString()] = l;
}
}
int main() {
IntWithString a(10, "ten");
IntWithString b(11, "ten");
appendMap(&a);
appendMap(&b);
return 0;
}
However when looking at the map m with the debugger I am getting a map that maps "ten" to a list of size 0. What I would like is a list of size 2.
I am not sure what you mean. If I do:
std::cout << m.size() << ", " << m["ten"].size() << std::endl;
I get this output:
1, 2
which is a map with one key ("ten"), and two values for that key (10 and 11), as expected.
Live demo
PS: Storing pointers in a container like this is a bit uncommon in C++. If you really want to do this though, consider to use Smart Pointers.
I want to write a program which reads names in a vector. After that it should read ages into another vector. (that's done)
The first element of the name-vector should be connected to the first element of the age-vector, so if I use any kind of sort() function on the name-vector the age-vector gets sorted as well.
Is there any way to realize this in an easy way?
class Name_pairs {
public:
//member functions
int read_names();
int read_ages();
int print();
private:
vector<double> age;
vector<string> name;
};
int Name_pairs::read_names() {
cout << "Please enter different names, you want to store in a vector:\n";
for (string names; cin >> names;) {
name.push_back(names);
}
cin.clear();
cout << "You entered following names:\n\t";
for (int i = 0; i < name.size(); i++) {
cout << name[i] << " \n\t";
}
return 0;
}
int Name_pairs::read_ages() {
cout << "\nPlease enter an age for every name in the vector.\n";
for (double ages; cin >> ages;) {
age.push_back(ages);
}
return 0;
}
I think you need a std::vector of a coupled type.
struct Name_pair {
double age;
string name;
};
Than you can use std::vector<Name_pair> and use a lambda
auto comparator = [](const Name_pair& first, const Name_pair& second){return first.age <second.age;};
to sort your vector with std::sort.
Name_pairs = std::vector<Name_pair>;
// fill vector
std::sort(Name_pairs.begin(), Name_pairs.end(), comparator);
Here is a working example.
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
struct Name_pair {
double age;
std::string name;
};
int main() {
std::vector<Name_pair> Name_pairs{{13, "Hallo"}, {32, "Welt"}, {1, "Georg"}};
auto comparator = [](const Name_pair& first, const Name_pair& second) { return first.age < second.age; };
std::sort(Name_pairs.begin(), Name_pairs.end(), comparator);
for (const auto np : Name_pairs) {
std::cout << np.name << "\n";
}
}
It prints
Georg
Hallo
Welt
If you want to implement a data-oriented design by using separate vectors instead of a single vector of classes, you could use a vector of indeces and sort it.
The following is just an example:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
class Customers
{
std::vector<double> ages_;
std::vector<std::string> names_;
template <typename Comparator>
auto make_indeces(Comparator &&comp)
{
std::vector<size_t> indeces(names_.size());
std::iota(indeces.begin(), indeces.end(), 0);
std::sort(indeces.begin(), indeces.end(), comp);
return indeces;
}
template <typename Type>
void reorder_vector(std::vector<Type> &src, std::vector<size_t> const &indeces)
{
std::vector<Type> tmp;
tmp.reserve(src.size());
std::generate_n(
std::back_inserter(tmp), src.size(),
[&src, idx = indeces.cbegin()] () mutable {
return src[*(idx++)];
});
src = std::move(tmp);
}
public:
void add(std::string const &name, double age)
{
names_.push_back(name);
ages_.push_back(age);
}
void sort_by_names()
{
auto indeces = make_indeces([this] (size_t i, size_t j) {
return names_[i] < names_[j];
});
reorder_vector(names_, indeces);
reorder_vector(ages_, indeces);
}
void show_sorted_by_ages()
{
auto indeces = make_indeces([this] (size_t i, size_t j) {
return ages_[i] < ages_[j];
});
for (auto i : indeces)
std::cout << names_[i] << ' ' << ages_[i] << '\n';
}
void show()
{
for (size_t i = 0; i < names_.size(); ++i)
std::cout << names_[i] << ' ' << ages_[i] << '\n';
}
};
int main(void)
{
Customers c;
c.add("Adam", 23);
c.add("Eve", 21);
c.add("Snake", 66.6);
c.add("Apple", 3.14);
std::cout << "Sorted by ages (doesn't modify the internal order):\n";
c.show_sorted_by_ages();
std::cout << "\nInternal order:\n";
c.show();
c.sort_by_names();
std::cout << "\nInternal order after sorting by names:\n";
c.show();
}
Testable HERE.
I have following problem. My vector contains pairs of pairs (see example below).
In the example below I will push_back vector with some "random" data.
What will be best solution to delete the vector element if any of their values will be equal i.e. 100 and update value if less than 100.
i.e.
typedef std::pair<int, int> MyMap;
typedef std::pair<MyMap, MyMap> MyPair;
MyMap pair1;
MyMap pair2;
In first example I want to update this pair because pair1.first is less than 100
pair1.first = 0;
pair1.second = 101;
pair2.first = 101;
pair2.second = 101;
In second example I want to delete this pair because pair2.first is equal to 100
pair1.first = 0;
pair1.second = 101;
pair2.first = 100;
pair2.second = 101;
Using functor "check" I am able to delete one or more elements (in this example just one).
It is possible to increase every value of that pair by 1 using std::replace_if function?
Is there any function that will update this value if any of these values will be lower then "X" and delete if any of these values will be equal "X"?
I know how to do it writing my own function but I am curious.
#include "stdafx.h"
#include<algorithm>
#include<vector>
#include<iostream>
typedef std::pair<int, int> MyMap;
typedef std::pair<MyMap, MyMap> MyPair;
void PrintAll(std::vector<MyPair> & v);
void FillVectorWithSomeStuff(std::vector<MyPair> & v, int size);
class check
{
public:
check(int c)
: cmpValue(c)
{
}
bool operator()(const MyPair & mp) const
{
return (mp.first.first == cmpValue);
}
private:
int cmpValue;
};
int _tmain(int argc, _TCHAR* argv[])
{
const int size = 10;
std::vector<MyPair> vecotorOfMaps;
FillVectorWithSomeStuff(vecotorOfMaps, size);
PrintAll(vecotorOfMaps);
std::vector<MyPair>::iterator it = std::find_if(vecotorOfMaps.begin(), vecotorOfMaps.end(), check(0));
if (it != vecotorOfMaps.end()) vecotorOfMaps.erase(it);
PrintAll(vecotorOfMaps);
system("pause");
return 0;
}
std::ostream & operator<<(std::ostream & stream, const MyPair & mp)
{
stream << "First:First = " << mp.first.first << " First.Second = " << mp.first.second << std::endl;
stream << "Second:First = " << mp.second.first << " Second.Second = " << mp.second.second << std::endl;
stream << std::endl;
return stream;
}
void PrintAll(std::vector<MyPair> & v)
{
for (std::vector<MyPair>::iterator it = v.begin(); it != v.end(); ++it)
{
std::cout << *it;
}
}
void FillVectorWithSomeStuff(std::vector<MyPair> & v, int size)
{
for (int i = 0; i < size; ++i)
{
MyMap m1(i + i * 10, i + i * 20);
MyMap m2(i + i * 30, i + i * 40);
MyPair mp(m1, m2);
v.push_back(mp);
}
}
Use std::stable_partition, along with std::for_each:
#include <algorithm>
//...partition the elements in the vector
std::vector<MyPair>::iterator it =
std::stable_partition(vecotorOfMaps.begin(), vecotorOfMaps.end(), check(0));
//erase the ones equal to "check"
vecotorOfMaps.erase(vecotorOfMaps.begin(), it);
// adjust the ones that were left over
for_each(vecotorOfMaps.begin(), vecotorOfMaps.end(), add(1));
Basically, the stable_partition places all the items you will delete in the front of the array (the left side of the partiton it), and all of the other items to the right of it.
Then all that is done is to erase the items on the left of it (since they're equal to 100), and once that's done, go through the resulting vector, adding 1 to eac
I have a TreeVertex class:
// TreeVertex.h
#ifndef __TREEVERTEX__
#define __TREEVERTEX__
#include <list>
using namespace std;
class TreeVertex {
public:
TreeVertex(list<int>, TreeVertex* = NULL);
list<int> getItemset();
private:
list<int> Itemset;
TreeVertex * Parent;
TreeVertex * LeftChild;
TreeVertex * RightSibling;
};
#endif // __TREEVERTEX__
// TreeVertex.cpp
#include "TreeVertex.h"
TreeVertex::TreeVertex(list<int> Itemset, TreeVertex* Parent) : Itemset(Itemset), Parent(Parent), LeftChild(NULL),
RightSibling(NULL) { }
list<int>
TreeVertex::getItemset() {
return Itemset;
}
And a main function like this:
#include <iostream>
#include "TreeVertex.h"
using namespace std;
int main (int argc, const char ** const argv)
{
list<int> tmpList1;
tmpList1.push_back(1);
TreeVertex * tmpTreeVert1 = new TreeVertex(tmpList1);
list<int> tmpList2;
tmpList2.push_back(2);
TreeVertex * tmpTreeVert2 = new TreeVertex(tmpList2);
list<int> newVertItemset;
newVertItemset.push_back(tmpTreeVert1->getItemset().front());
newVertItemset.push_back(tmpTreeVert2->getItemset().front());
cout << newVertItemset.front() << " " << newVertItemset.back() << endl;
TreeVertex * newTreeVert = new TreeVertex(newVertItemset);
cout << newTreeVert->getItemset().front() << " " << newTreeVert->getItemset().back() << endl;
for (list<int>::iterator it = newTreeVert->getItemset().begin(); it != newTreeVert->getItemset().end(); ++it) {
cout << (*it) << " ";
}
cout << endl;
cout << newTreeVert->getItemset().size() << endl;
return 0;
}
The output looks like this:
1 2
1 2
2
2
The next to the last output (the first single "2"), should be "1 2" just like the others.
Any ideas why the iterator is not going over the first element?
Thanks.
The problem with this:
list<int>
TreeVertex::getItemset() {
return Itemset;
}
Everytime you call this function, it returns a copy of the object, which means the following loop should not work:
for (list<int>::iterator it = newTreeVert->getItemset().begin();
it != newTreeVert->getItemset().end(); ++it) {
as it compares iterators from two different objects. A solution is to return reference as:
list<int> & //<--- return reference, not copy
TreeVertex::getItemset() {
return Itemset;
}
But a better solution is to remove getItemset altogether and instead of that, add begin() and end() member functions as:
//define these typedefs first in the public section
typedef list<int>::iterator iterator;
typedef list<int>::const_iterator const_iterator;
iterator begin() { return itemSet.begin(); }
iterator end() { return itemSet.end(); }
and then write the for loop as:
for(TreeVertex::iterator it = newTreeVert->begin();
it != newTreeVert->end(); ++it) {
If you can use C++11, then you should add these:
//note : the function names start with `c`
const_iterator cbegin() const { return itemSet.cbegin(); }
const_iterator cend() const { return itemSet.cend(); }
Or, if you use C++03 (and cannot use C++11), then add these:
const_iterator begin() const { return itemSet.begin(); }
const_iterator end() const { return itemSet.end(); }
In the program below I've a typedef map. What I want to do is to implement a hash table. I'm trying to use unordered_map since I heard that is the efficient as it takes O(1) time. I use my typedef map everywhere in my main program (another program that I'm working on) so I don't want to change that. I want to implement hash table in one of the functions and I'm trying to figure out how to insert the contents of my map into the hash table and search for the key later. I've inserted a comment in two places where I'm having trouble. Please help.
#include <iostream>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <unordered_map>
using namespace std;
typedef vector<int> v_t;
typedef set<int> s_t;
typedef map<s_t, v_t> m_t;
typedef m_t::iterator m_it;
typedef std::unordered_map<s_t, v_t> Mymap;
int main(){
m_t sample;
for (int i = 0; i < 100; i = i+2) {
v_t v;
for(int k = 100 ; k<=105 ; ++k)
v.push_back(k);
s_t k;
k.insert(i);
sample.insert(sample.end(), make_pair(k, v));
}
//---------Debug--------------------
for( m_it it(sample.begin()) ; it!=sample.end(); ++it) {
cout << "Key: ";
copy(it->first.begin(), it->first.end(), ostream_iterator<int>(cout, " "));
cout << " => Value: ";
copy (it->second.begin(),it->second.end(),ostream_iterator<double>(cout," "));
cout << endl;
}
//---------------------------------
Mymap c1;
for( m_it it(sample.begin()) ; it!=sample.end(); ++it) {
c1.insert(Mymap::value_type(it->first,it->second)); // how to do this ?
}
s_t s;
s.insert(72);
if(c1.find(s)!=c1.end()) // does this work ?
cout << "Success" << endl;
return 0;
}
I appreciate any help or comments.
After reading Jason's comments I understand why i cannot use a std::set as a key in unordered_map so I tried to use std::string as a key but the find function won't work. Could you please help me.
Mymap c1;
for( m_it it(sample.begin()) ; it!=sample.end(); ++it) {
v_t v1;
std::string key;
key.insert(key.begin(),it->first.begin(),it->first.end());
copy(it->second.begin(), it->second.end(),std::back_inserter(v1));
c1.insert(Mymap::value_type(std::make_pair(key,v1)));
}
string s = "72";
if((c1.find(s) != c1.end()) == true)
cout << "Success" << endl;
return 0;
The basic element you're missing to make this work is to define a hashing function for your std::set that you're using as the key. The STL already defines equality and lexicographical ordering for a std::set, so you can use it as the key-value in a std::map as-is without any problems. It does not define a hash function though, so that is something you're going to have to-do by overloading std::hash. This is fairly straight-forward, and can be done by defining the following function:
namespace std
{
template<>
struct hash<std::set<int> > : public std::unary_function<std::set<int>, size_t>
{
size_t operator()(const std::set<int>& my_set) const
{
//insert hash algorithm that returns integral type
}
};
}
The above functor object would return an integral type of size_t, and would take a std::set as the argument. You'll have to define it inside of namespace std so that std::unordered_map will recognize it. An "easy" algorithm could be simply summing the elements since you have a set of type int. There are more complex algorithms out there that would reduce the number of collisions such a simple algorithm would create at the expense of hashing time. Once you have this defined though, you shouldn't have any problems inserting your std::set key-values into an unordered_map, as well as creating new key-values and finding them in the hash table.
You can see an example of your source-code working at: http://ideone.com/DZ5jm
EDIT: Jason's code placed here for reference:
#include <iostream>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <unordered_map>
using namespace std;
namespace std
{
template<>
struct hash<set<int> > : public unary_function<set<int>, size_t>
{
size_t operator()(const std::set<int>& my_set) const
{
set<int>::iterator iter = my_set.begin();
int total = 0;
for (; iter != my_set.end(); iter++)
{
total += *iter;
}
return total;
}
};
}
typedef vector<int> v_t;
typedef set<int> s_t;
typedef map<s_t, v_t> m_t;
typedef m_t::iterator m_it;
typedef std::unordered_map<s_t, v_t> Mymap;
int main(){
m_t sample;
for (int i = 0; i < 100; i = i+2) {
v_t v;
for(int k = 100 ; k<=105 ; ++k)
v.push_back(k);
s_t k;
k.insert(i);
sample.insert(sample.end(), make_pair(k, v));
}
//---------Debug--------------------
for( m_it it(sample.begin()) ; it!=sample.end(); ++it) {
cout << "Key: ";
copy(it->first.begin(), it->first.end(), ostream_iterator<int>(cout, " "));
cout << " => Value: ";
copy (it->second.begin(),it->second.end(),ostream_iterator<double>(cout," "));
cout << endl;
}
//---------------------------------
Mymap c1;
for( m_it it(sample.begin()) ; it!=sample.end(); ++it) {
c1.insert(Mymap::value_type(it->first,it->second)); // how to do this ?
}
s_t s;
s.insert(72);
if(c1.find(s)!=c1.end()) // does this work ?
cout << "Success" << endl;
return 0;
}