I tried STL sample program using "map".
http://ideone.com/LB8xvh
#include <iostream>
#include <map>
#include <cstring>
using namespace std;
class ItemName
{
char name[80];
public:
ItemName(char *s) { strcpy(name, s); }
char *get() { return name; }
};
bool operator<(ItemName a, ItemName b)
{
return strcmp(a.get(), b.get()) < 0;
}
class ItemObj
{
char str[80];
public:
ItemObj(char *s) { strcpy(str, s); }
char *get() { return str; }
};
char itemdata[][80] = {
"potion", "heal HP",
"key", "unlock a door",
"lamp", "light",
};
int main() {
map<ItemName, ItemObj> items;
for(int i=0; i<3; i++) {
items.insert(
pair<ItemName, ItemObj>(
ItemName(itemdata[i*2]),
ItemObj(itemdata[i*2+1]))); // ***** pair *****
}
map<ItemName, ItemObj>::iterator p;
char str[80];
const int kMaxLoop = 5;
int nLoop = 0;
while(nLoop < kMaxLoop) {
cout << "> ";
cin >> str;
p = items.find(str);
if(p != items.end() ) {
cout << p->second.get() << endl;
} else {
cout << "unknown item." << endl;
}
nLoop++;
}
return 0;
}
In this example, I am not quite sure where the operator "<" is used.
If I comment out the definition of the operator "<", I receive lots of errors.
std::map has a parameter to specify how to compare elements in the map (needed because a map always maintains its contents sorted in order by key). By default, that's std::less<T>.
std::less<T>, in turn, will do the comparison using operator<.
You can create a map of items for which operator< isn't defined, but to do it you need to specify the comparison function/functor explicitly.
That said: your ItemData and ItemObj are both really just doing things that std::string can already do. You could reduce most of the code above to something like this:
std::map<std::string, std::string> items{
{ "potion", "heal HP" },
{ "key", "unlock a door" },
{ "lamp", "light" }
};
It is used internally by the map to place and find entries. Otherwise, find would have to compare the key you supply it against literally every single other entry one by one and you couldn't iterate the map in key order.
Basically, maps efficiently store elements in order. To do that, they have to have some way to know what the order is, and they do that by calling operator< (unless you specify otherwise).
Related
I've recently read up on STL functions in C++. I understand the basic uses of the functions, but I am struggling getting them to use member variables of a struct.
I have this struct:
struct Apples
{
double weight; // oz
string color; // red or green
void print() const { cout << color << ", " << weight << endl; }
};
Basically, I insert Apples into a vector storing random weights and random color. Now, I want to use a count_if function to determine how many apples are greater than a given weight. I want to convert a function like this:
int cnt = 0;
for(auto it = crate.cbegin(); it != crate.cend(); ++it)
if(it->weight > toFind)
cnt++;
to a count_if() version (this does not work):
int cnt = count_if(crate.begin(), crate,end(), isGreater())
With isGreater() being like this:
void isGreater()
{
if(it->weight > toFind)
return it->weight > toFind;
}
What I don't understand about STL functions and a struct is how to use the member variables inside of the struct with the STL functions. I'm not sure what to pass inside of the STL function, either. Would it be better to use a lambda function in this case? If so, why?
Here is all the current code, if it doesn't make sense:
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <deque>
#include <string>
using namespace std;
struct Apples
{
double weight; // oz
string color; // red or green
void print() const { cout << color << ", " << weight << endl; }
};
void isGreater()
{
if(it->weight > toFind)
return it->weight > toFind;
}
int main()
{
srand(time(nullptr));
const double minWeight = 8.;
const double maxWeight = 3.;
cout << "Input crate size: ";
int size;
cin >> size;
vector <Apples> crate(size);
for(auto it = crate.begin(); it != crate.end(); ++it)
{
it->weight = minWeight + static_cast<double>(rand())/RAND_MAX*(maxWeight - minWeight);
it->color = rand() % 2 == 1 ? "green" : "red";
}
cout << "Enter weight to find: ";
double toFind;
cin >> toFind;
//this is what I want to convert to count if
int cnt = 0;
for(auto it = crate.cbegin(); it != crate.cend(); ++it)
if(it->weight > toFind)
cnt++;
std::count_if takes unary predicate as the third argument. In this case unary predicate is a function taking one object and returning true if object matches find criterion or false if not.
Since your criterion depends on toFind, it seems more laconic to use lambda capturing toFind:
int cnt = count_if(crate.begin(), crate.end(), [toFind](const Apple& apple) {
return it->weight > toFind;
});
If you want a standalone function, you can use:
bool isGreater(double toFind, const Apple& apple) {
return it->weight > toFind;
}
...
int cnt = count_if(crate.begin(), crate.end(),
std::bind(&isGreater, toFind, std::placeholders::_1));
Note, that you don't need to call function, you need to pass it:
int cnt = count_if(crate.begin(), crate,end(), isGreater())
// ^^ remove parentheses
you are not storing the apples in the vector.
you have to initialize inside a loop each apple and then store them in the vector.
crate.push_back(newApple).
so run a loop from 0 to size.
inside that loop initialize new apples and give them weights and colors
then push_back in vector:
for(int i = 0; i < size ++i)
{
apples newApple;
newApple.weight = ...;
newApple.color = ...;
crate.push_back(newApple);
}
This is usually accomplished by creating a "functor" class, a class whose objects can be called like a function. Each instance call hold the reference weight:
struct IsGreater {
double w;
IsGreater(double weight) : w{weight} {}
bool operator()(const Apples& A) const {
return A.weight > w;
}
};
Then we just need to create an instance of the class holding the reference weight and pass it to count_if:
const int count = std::count_if(crate.begin(), crate.end(), IsGreater(toFind));
You can avoid creating an explicit class using a lambda:
const int count = std::count_if(crate.begin(), crate.end(),
[=](const Apples& A) -> bool {
return A.weight > toFind;
});
Here the reference value toFind is captured by value.
I have a class Test which contains two vectors of a Letter class, a user defined type for which the less-than operator (<) has been implemented. How can I best generate all possible permutations of Test?
class Test
{
vector<Letter> letter_box_a;
vector<Letter> letter_box_b;
}
So if letter_box_a contains the letters A and B and letter_box_b contains C and D the valid permutations of Test would be (AB)(CD), (BA)(CD), (AB)(DC) and (BA)(DC).
Although I am able to brute force it, I was trying to write a better (more elegant/efficient) function which would internally call std::next_permutation on the underlying containers allowing me to do
Test test;
while (test.set_next_permutation())
{
// Do the stuff
}
but it appears to be a bit trickier than I first anticipated. I don't necessarily need an STL solution but would like an elegant solution.
I would think you could do something like
bool Test::set_next_permutation() {
auto &a = letter_box_a, &b = letter_box_b; // entirely to shorten the next line
return std::next_permutation(a.start(), a.end()) || std::next_permutation(b.start(), b.end());
}
(Of course, a while loop will skip the initial permutation in any case. You want a do...while loop instead.)
If you want to use std::next_permutation, you need a nested loop for each vector you are permuting:
std::string s0 = "ab";
std::string s1 = "cd";
do
{
do
{
cout << s0 << "" << s1 << endl;
} while (std::next_permutation(s0.begin(), s0.end()));
} while (std::next_permutation(s1.begin(), s1.end()));
Output:
abcd
bacd
abdc
badc
And, in the class:
class Foo
{
public:
Foo(std::string_view arg_a, std::string_view arg_b)
: a(arg_a)
, b(arg_b)
, last(false)
{ }
void reset_permutations()
{
last = false;
}
bool next_permutation(std::string& r)
{
if (last)
return false;
if (not std::next_permutation(a.begin(), a.end()))
if (not std::next_permutation(b.begin(), b.end()))
last = true;
r = a + b;
return true;
}
private:
std::string a, b;
bool last;
};
int main(int argc, const char *argv[])
{
Foo foo("ab", "cd");
string s;
while (foo.next_permutation(s))
cout << s << endl;
return 0;
}
Basically, say, I have the following data:
(let me note that the columns change with every piece of data I get, i.e. I need to keep things general and cannot restrict my solution to only Tenor, Date, etc.)
Now I want to be able to represent and conveniently access this data in an object/class in C++.
I have been playing around with map a bit:
#include <iostream>
#include <map>
#include <string>
using namespace std;
class my_table {
private:
map<string, map<string, string>> c;
public:
void set(string key1, string key2, string value){ this->c[key1][key2] = value; }
string get(string key1, string key2){
map<string, map<string, string>>::iterator it = this->c.find(key1);
if (it != this->c.end()){
map<string, string>::iterator it2 = this->c[key1].find(key2);
if (it2 != this->c[key1].end()){
return c[key1][key2];
}
return "n/a";
}
return "n/a";
}
};
void main() {
my_table a;
a.set("1", "Tenor", "1D");
cout << a.get("1", "Tenor") << endl; // returns '1D'
cout << a.get("2", "Tenor") << endl; // returns 'n/a'
cout << a.get("1", "Rate") << endl; // returns 'n/a'
}
But I am not overly satisfied with this implemenation. In particular, I would want to be able to do things like:
a.get("Tenor","3M", "Rate") // should return '1.6%'
a.get("Date","01-Jan-2016", "Responsibility") // should return 'MG'
a.get_all("Type","Forward", "Rate") // should return an array {1.3%,2.4%}
a.get_row(4) // should return an array {4M,...,2.0%,MG}
And:
I am wondering whether there are there any standard packages that could help me simplify this implementation overall?
In particular, my get function seems unnecessarily cumbersome.
And generally, is map is even the right way to go in terms of storing data like this?
And what if I wanted to generalise this implemenation to more than just 2 keys? Maybe 3 keys. My solution is quite rigid
enum struct Type {
Spot
Forward
}
struct Row {
string tenor;
Date date;
int convention;
Type type;
double rate;
ResposibilityType responsibility;
};
std::vector<Row> table = {
[...]
}
access you do with std::find_if. Tables in databases might be stored like this internally. If you want multiple primary keys you can create for each key a map that maps from the primary key to an element in table. If you want a combined key, you need tuple like this std::map<std::pair<Key1,Key2>, Row*>
How about the matrix type from boost.ublas? You can create a simple enum type to easily reference columns.
For querying you can probably build something quick via the filter_iterator.
Hope this helps!
Edit: Sorry didn't notice your comment. A quick hack I can think of to support dynamic column size is using a hash map for storing column name to column index mapping in a separate hash map. Good luck!
Limiting yourself to maps could overcomplicate this somewhat. If I understand this correctly, the data structure is completely undefined at compile time. In that case perhaps a simpler way to implement it is as a vector of hash-key-value triples, like this:
#include "stdafx.h"
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class HashKeyValue
{
private:
string hash;
string key;
string value;
public:
HashKeyValue() {}
HashKeyValue(string h, string k, string v)
{
hash = h;
key = k;
value = v;
}
string getHash() { return hash; }
string getKey() { return key; }
string getValue() { return value; }
};
class my_table
{
private:
vector<HashKeyValue> hkv;
public:
my_table() {}
void set(string h, string k, string v)
{
hkv.push_back(HashKeyValue(h, k, v));
}
string getV(string h, string k)
{
for (unsigned int i = 0; i < hkv.size(); i++)
{
if (hkv[i].getHash() == h && hkv[i].getKey() == k)
return hkv[i].getValue();
}
return "n/a";
}
string getByColValue(string col1, string val, string col2)
{
string hash;
int got = 0;
for (unsigned int i = 0; i < hkv.size() && !got; i++)
{
if (hkv[i].getKey() == col1 && hkv[i].getValue() == val)
{
hash = hkv[i].getHash();
got = 1;
}
}
if (got)
{
for (unsigned int i = 0; i < hkv.size(); i++)
{
if (hkv[i].getHash() == hash && hkv[i].getKey() == col2)
return hkv[i].getValue();
}
return "n/a";
}
else return "n/a";
}
};
int main()
{
my_table m;
m.set("1", "Tenor", "1D");
m.set("3", "Tenor", "3M");
m.set("3", "Rate", "1.6%");
cout << "get-1-Tenor(1D): " << m.getV("1", "Tenor") << endl;
cout << "get-1-Alto(n/a): " << m.getV("1", "Alto") << endl;
cout << "get-3-Rate(1.6%): " << m.getV("3", "Rate") << endl;
cout << "getBCV-Tenor-3M-Rate(1.6%): " << m.getByColValue("Tenor", "3M", "Rate") << endl;
return 0;
}
Hopefully getByColValue() makes sense; it first looks up the hash, then looks up the Rate for that hash. The hash is what relates each key-value pair to others on the same row. It shouldn't be too tricky to change getByColValue() to return a vector<string> instead, for the getByColValue("Type","Forward","Rate") case: just make hash a vector<string> instead, define the return type as another vector<string>, and a few other tweaks.
This also makes the implementation of getRow() fairly trivial; just loop over hkv where hash==rowid and bung the key/value pairs (or just the values) into a vector.
I'm trying to write function that search for char * element in array of char* and the function start check this element, if the element exist in the array I will have "found", if not it should be "inserted" and the element added to the array.
I wrote this code but I cannot know how to try it, the program always gives me exception, what can I do to check the element in my pointer array?
void checkFunction(char*myArray[], char *element,bool flag)
{
for (int i = 0; i < strlen(*myArray) ; ++i)
{
if (myArray[i] == element)
{
flag = true;
}
}
*myArray = element;
flag = false;
if (flag)
{
cout << "Found" << endl;
}
else
{
cout << "Inserted" << endl;
}
}
C++ Way
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(int argc, const char * argv[]) {
vector<string> myStrings { "One", "Two", "Three" };
// std::find() finds the first element that matches a value
auto it = find(begin(myStrings), end(myStrings), "Twooo");
if (it != end(myStrings)) {
cout << "We found this string; do something..." << endl;
}
}
Few remarks regarding your function:
1.Why do you need the third parameter bool flag, instead of having it as local variable?
2.If you want to expand an array you should copy the old to a newly allocated and then add the new element, you can not just do: *myArray = element;
3.If you want to iterate through the array length/ size, instead of:
for (int i = 0; i < strlen(*myArray) ; ++i)
pass an additional parameter to your function, that indicates the number of elements in the array.
With std::string and std::vector you could do something like:
void check_insert (std::vector<std::string>& v, std::string& c) {
for (auto i = 0; i < v.size(); ++i) {
if (v[i] == c) {
std::cout << "Found!\n";
return;
}
}
v.push_back(c);
std::cout << "Inserted!\n";
}
I have a custom class 'team' and one of its attributes is its 'name.' After each 'team' is created, I add it to a vector teamList.
I would like to implement a function that continuously prompts the user for a team name which is not already taken by a team within the teamList. I have the following code:
while (true) {
string newString;
bool flag = true;
getline(cin, newString);
for (int i = 0; i < teamList.size(); i++) {
if (teamList[i].name.compare(newString) == 0) flag = false;
}
if (flag == true) {
return newString;
} else {
cout << "name already taken." << endl;
}
}
However, this code is really ugly; is there a better way to check? Also, a more general question- faced with an issue of ugly code (like this one), what kinds of steps can I take to find a new, cleaner implementation? Thanks.
I would use std::set, which deals with duplicates for you. As an example, you can see that the class is sorted by the string member, and when three are inserted in main, only two stay because two of the insertions have the same string, so they are treated equal.
#include <iostream>
#include <set>
#include <string>
struct SetThing {
SetThing(int value, const std::string &value2) : i(value), s(value2){}
int i;
std::string s;
bool operator<(const SetThing &other) const {
return s < other.s;
}
};
int main() {
std::set<SetThing> s;
s.insert(SetThing(5, "abc"));
s.insert(SetThing(4, "def"));
s.insert(SetThing(6, "abc"));
std::cout << s.size();
}
Now for inserting, you can just reprompt while the second member of the returned pair is false:
do {
//get input
} while (!teamList.insert(somethingBasedOnInput).second);
define an equality operator in team that can compare a team to a string:
bool team::operator==(string s) const
{
return(s==name);
}
Then you can use find:
vector<team>::const_iterator itr = find(teamList.begin(), teamList.end(),
newString);
if(itr!=league.end())
cout << "name already taken" << endl;