I need to check if two substrings are equal while inserting to a map. Here is the code:
class substring {
public:
substring(string* str, int offset, int length) : str(str), offset(offset), length(length) { }
bool operator < (const substring& val) const {
if (str->compare(offset, length, *val.str, val.offset, val.length) == 0) return false;
else return true;
}
int offset, length;
string* str;
};
This class above is a 'key' in my map. Lengths of both substrings are always same. Some of the conditions are wrong, cause it's still yelling 'invalid comparator'.
your if statement in comparation function code is convoluted way to say:
return str->compare(offset, length, *val.str, val.offset, val.length) != 0;
which is incorrect for comparison function that std::map requires. Remember you are implementing less than operator, not equivalence. If you want your substring to be sorted in ascending order use this:
return str->compare(offset, length, *val.str, val.offset, val.length) < 0;
I would recommend using const reference to std::string in you substring class - that will reflect the fact you do not accept nullptr as pointer and show intent that you do not want to change original string through this class and make your code cleaner.
Related
I have a simple function, but it outputs the above error on compile. I looked at similar posts, but I have included the type in my declaration correctly as far as I can see. Am I just missing something simple here?
Header:
int LinearSearch (const vector <Die> & searchVector, const <Die> & targetVal);
Implementation:
int Game::LinearSearch (const vector <Die> & searchVector, const <Die> & targetVal) {
// Simple linear search function which will return the index of targetVal in the vector
// Returns null if the value isn't found
for (int i=0; i < searchVector.size(); i++) {
if (targetVal.pips == searchVector[i].pips) return i;
}
return nullptr;
}
The angle brackets on the second parameter are wrong, since they are not being used to specialize a templated class like in the first parameter (std::vector<Die> is a specialization of the std::vector templated class), so you need to remove them in the second parameter.
Also, you cannot return nullptr as an int, and even if you could it would be wrong to do so. Your loop returns the index of the matching element in the vector, so nullptr could be mistaken for index 0. A more appropriate value to return when no match is found would be -1 instead.
int LinearSearch (const vector<Die> &searchVector, const Die &targetVal);
int Game::LinearSearch (const vector<Die> &searchVector, const Die &targetVal) {
// Simple linear search function which will return the index of targetVal in the vector
// Returns -1 if the value isn't found
for (int i=0; i < searchVector.size(); i++) {
if (targetVal.pips == searchVector[i].pips) return i;
}
return -1;
}
That being said, you should have a look at the std::find_if() standard algorithm function.
I am trying to use std::string as a key in the stxxl::map
The insertion was fine for small number of strings about 10-100.
But while trying to insert large number of strings about 100000 in it, I am getting segmentation fault.
The code is as follows:
struct CompareGreaterString {
bool operator () (const std::string& a, const std::string& b) const {
return a > b;
}
static std::string max_value() {
return "";
}
};
// template parameter <KeyType, DataType, CompareType, RawNodeSize, RawLeafSize, PDAllocStrategy (optional)>
typedef stxxl::map<std::string, unsigned int, CompareGreaterString, DATA_NODE_BLOCK_SIZE, DATA_LEAF_BLOCK_SIZE> name_map;
name_map strMap((name_map::node_block_type::raw_size)*3, (name_map::leaf_block_type::raw_size)*3);
for (unsigned int i = 0; i < 1000000; i++) { /// Inserting 1 million strings
std::stringstream strStream;
strStream << (i);
Console::println("Inserting: " + strStream.str());
strMap[strStream.str()]=i;
}
In here I am unable to identify why I am unable to insert more number of strings. I am getting segmentation fault exactly while inserting "1377". Plus I am able to add any number of integers as key. I feel that the variable size of string might be causing this trouble.
Also I am unable to understand what to return for max_value of the string. I simply returned a blank string.
According to documentation:
CompareType must also provide a static max_value method, that returns a value of type KeyType that is larger than any key stored in map
Because empty string happens to compare as smaller than any other string, it breaks this precondition and may thus cause unspecified behaviour.
Here's a max_value that should work. MAX_KEY_LEN is just an integer which is larger or equal to the length of the longest possible string key that the map can have.
struct CompareGreaterString {
// ...
static std::string max_value() {
return std::string(MAX_KEY_LEN, std::numeric_limits<unsigned char>::max());
}
};
I have finally found the solution to my problem with great help from Timo bingmann, user2079303 and Martin Ba. Thank you.
I would like to share it with you.
Firstly stxxl supports POD only. That means it stores fixed sized structures only. Hence std::string cannot be a key. stxxl::map worked for about 100-1000 strings because they were contained in the physical memory itself. When more strings are inserted it has to write on disk which is internally causing some problems.
Hence we need to use a fixed string using char[] as follows:
static const int MAX_KEY_LEN = 16;
class FixedString {
public:
char charStr[MAX_KEY_LEN];
bool operator< (const FixedString& fixedString) const {
return std::lexicographical_compare(charStr, charStr+MAX_KEY_LEN,
fixedString.charStr, fixedString.charStr+MAX_KEY_LEN);
}
bool operator==(const FixedString& fixedString) const {
return std::equal(charStr, charStr+MAX_KEY_LEN, fixedString.charStr);
}
bool operator!=(const FixedString& fixedString) const {
return !std::equal(charStr, charStr+MAX_KEY_LEN, fixedString.charStr);
}
};
struct comp_type : public std::less<FixedString> {
static FixedString max_value()
{
FixedString s;
std::fill(s.charStr, s.charStr+MAX_KEY_LEN, 0x7f);
return s;
}
};
Please note that all the operators mainly((), ==, !=) need to be overriden for all the stxxl::map functions to work
Now we may define fixed_name_map for map as follows:
typedef stxxl::map<FixedString, unsigned int, comp_type, DATA_NODE_BLOCK_SIZE, DATA_LEAF_BLOCK_SIZE> fixed_name_map;
fixed_name_map myFixedMap((fixed_name_map::node_block_type::raw_size)*5, (fixed_name_map::leaf_block_type::raw_size)*5);
Now the program is compiling fine and is accepting about 10^8 strings without any problem.
also we can use myFixedMap like std::map itself. {for ex: myFixedMap[fixedString] = 10}
If you are using C++11, then as an alternative to the FixedString class you could use std::array<char, MAX_KEY_LEN>. It is an STL layer on top of an ordinary fixed-size C array, implementing comparisons and iterators as you are used to from std::string, but it's a POD type, so STXXL should support it.
Alternatively, you can use serialization_sort in TPIE. It can sort elements of type std::pair<std::string, unsigned int> just fine, so if all you need is to insert everything in bulk and then access it in bulk, this will be sufficient for your case (and probably faster depending on the exact case).
This question already has answers here:
How to check if a string is in a list of strings?
(8 answers)
Closed 11 months ago.
#include <iostream>
#include <string>
using namespace std;
bool in_array(string value, string *array)
{
int size = (*array).size();
for (int i = 0; i < size; i++)
{
if (value == array[i])
{
return true;
}
}
return false;
}
int main() {
string tab[2] = {"sdasd", "sdsdasd"};
string n;
cin >> n;
if (in_array(n, tab)) {
}
return 0;
}
I want to check in C++ if n string is in tab array, but the code return an error.
What I am doing wrong? Maybe I should use the vectors?
int size = (*array).size();
It will not tell you the size of array, it tells you the length of first string in that array, you should pass the length of array to the function separately. The function should look like:
bool in_array(string value, string *array, int length)
But a better choice is using std::vector and std::find:
#include <vector>
#include <algorithm>
bool in_array(const std::string &value, const std::vector<std::string> &array)
{
return std::find(array.begin(), array.end(), value) != array.end();
}
and then, you can use it like:
std::vector<std::string> tab {"sdasd", "sdsdasd"};
if (in_array(n, tab))
{
...
}
When passing an array as an argument to a function which takes only a pointer, you can't query the size of the array within the function (since it got converted to a "stupid" pointer to the first element, nothing more). You typically add a "count" parameter to your signature or an "end" iterator instead.
What you're trying to implement is basically std::find. It takes two iterators (begin and end of the sequence) and the element to be found. Simply use this function.
std::find(std::begin(tab), std::end(tab), n);
will return an iterator to the element if it was found, the end iterator otherwise. Checking for equality with the end iterator will tell you if the element was found in the array.
If you don't like the "iterator interface" of the std algorithms, you can achieve your PHP-like signature by wrapping around std::find by using a template function:
template<class Element, class Container>
bool in_array(const Element & element, const Container & container)
{
return std::find(std::begin(container), std::end(container), element)
!= std::end(container);
}
Please note: This answer assumes C++11. If you use an older compiler, it might not work or it only works if you add -std=c++11 to the compiler flags.
masoud answer is correct but it overly complicated. all you need is this.
bool isInVect=false;
isInVect = std::find(vector.begin(), vector.end(), stringToFind) != vector.end();
if (isInVect == true)
{
cout << "Found string in Vector ..." << endl;
}
I'd like to sort a vector so that the capital letters follow the lower case letter. If I have something like
This is a test
this is a test
Cats
cats
this thing
I would like the output to be
cats
Cats
this is a test
This is a test
this thing
The standard library sort will output
Cats
This is a test
cats
this is a test
this thing
I want to pass a predicate to std::sort so that it compares the lowercase version of the strings that I pass as arguments.
bool compare(std::string x, std::string y)
{
return lowercase(x) < lowercase(y);
}
I tried lowering each character within the function and then making the comparison but it didn't work. I would like to test this approach by converting the string to lowercase by some other method. How do I convert strings into lowercase?
EDIT::
Actually I figured out the problem. This works. When I first wrote the function, instead of ref = tolower(ref) I had tolower(ref) without reassigning to ref so it wasn't doing anything.
bool compare(std::string x, std::string y)
{
for(auto &ref:x)
ref = tolower(ref);
for(auto &ref:y)
ref = tolower(ref);
return x < y;
}
EDIT::
This code actually sorts with the capital letter first sometimes and the capital letter second in other times so it doesn't solve the problem completely.
The usual way to do this would be to build a collation table. That's just a table giving the relative ordering of every character. In your case, you want each upper-case letter immediately following the corresponding lower-case letter.
We can do that something like this:
class comp_char {
std::vector<int> collation_table;
public:
comp_char() : collation_table(std::numeric_limits<unsigned char>::max()) {
std::iota(collation_table.begin(), collation_table.end(), 0);
for (int i = 0; i < 26; i++) {
collation_table['a' + i] = i * 2;
collation_table['A' + i] = i * 2 + 1;
}
}
bool operator()(unsigned char a, unsigned char b) {
return collation_table[a] < collation_table[b];
}
};
For the moment, I've ignored the (possibly knotty) problem of the relative ordering of letters to other characters. As it's written, everything else sorts before letters, but it would be pretty easy to change that so (for example) letters sorted before anything else instead. It probably doesn't make a huge difference either way though -- most people don't have strong expectations about whether 'a' < ';' or not.
In any case, once the collation table is built and usable, you want to use it to compare strings:
struct cmp_str {
bool operator()(std::string const &a, std::string const &b) {
comp_char cmp;
size_t i = 0;
while (a[i] == b[i] && i < a.size())
++i;
return cmp(a[i], b[i]);
}
};
...which we can use to do sorting, something like this:
int main(){
std::vector<std::string> inputs {
"This is a test",
"this is a test",
"Cats",
"cats",
"this thing"
};
std::sort(inputs.begin(), inputs.end(), cmp_str());
std::copy(inputs.begin(), inputs.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
For the moment, I've only written the collation table to handle the basic US-ASCII letters. For real use, you'd typically want to have things like letters with accents and such sort next to their corresponding un-accented equivalents. For that, you typically end up pre-building the table to (partially) match things like the Unicode specification for how things should be ordered.
Note that this output doesn't quite match what the original question says is desired, but I think in this case the question has a mistake. I can't see any way it would be even marginally reasonable to produce an order like:
this is a test
This is a test
this thing
This has "T" sorting both after and before "t", which doesn't seem to make sense (or at least doesn't fit with a lexical sort, which is what people nearly always want for strings).
The simplest solution is to use the collation-aware sorting provided by the standard locale object.
A locale's operator()(std::string, std::string) is exactly the locale's collation-aware comparison operator, so you can just insert it directly into your call to std::sort:
// Adjust to the locale you actually want to use
std::sort(strings.begin(), strings.end(), std::locale("en_US.UTF-8"));
Example on ideone
Your solution is almost there, you just need to make a special case if the lower case version of the strings are equal:
std::string to_lower(std::string s)
{
for (auto & c : s)
c = std::tolower(c);
return s;
}
bool string_comp(std::string const & lhs, std::string const & rhs)
{
auto lhs_lower = to_lower(lhs);
auto rhs_lower = to_lower(rhs);
if (lhs_lower == rhs_lower)
return rhs < lhs;
return lhs_lower < rhs_lower;
}
This could use some optimization. Copying the string is not necessary. You can, of course, do a case insensitive comparison in place. But that is feature is not conveniently available in the standard library, so I'll leave that exercise up to you.
To be clear, I was aiming at the usual lexicographic type comparison but somehow make uppercase follow the lowercase if the strings were identical otherwise.
This requires a two-steps comparison then:
compare the strings in case-insensitive mode
if two strings are equal in case-insensitive mode, we want the reverse result of a case sensitive comparison (which puts upper-case first)
So, the comparator gives:
class Comparator {
public:
bool operator()(std::string const& left, std::string const& right) {
size_t const size = std::min(left.size(), right.size());
// case-insensitive comparison
for (size_t i = 0; i != size; ++i) {
if (std::tolower(left[i]) < std::tolower(right[i])) { return true; }
}
if (left.size() != right.size()) { return size == left.size(); }
// and now, case-sensitive (reversed)
return right < left;
}
}; // class Comparator
You need to do the comparison one char at a time, stopping at the first different char and then returning the result depending on the case conversion first, and on original char otherwise:
bool mylt(const std::string& a, const std::string& b) {
int i=0, na=a.size(), nb=b.size();
while (i<na && i<nb && a[i]==b[i]) i++;
if (i==na || i==nb) return i<nb;
char la=std::tolower(a[i]), lb=std::tolower(b[i]);
return la<lb || (la==lb && a[i]<b[i]);
}
Warning: untested breakfast code
Either use locals that already have the ordering you want, or write a character by character comparison function then use std::lexicographical_compare to turn it into a string comparison function.
I would try locals first, but if that proved frustrating the lexicographic is not horrible.
To compare chqracters, create two tuples or pairs of lower_case_letter, unchanged_letter, and call < on it. This will first order by lower case, then if that fails by the unchanged. I forget what order the upper vs lower will sort in: but if the order is backwards, just swap which lower case letter gets paired with which upper case letter, and you'll reverse the order!
I have a struct like this:
struct db {
string name,sur;
int num;
};
And declared an array of db structs:
struct db a[10];
and every member of a[] is filled with a name, surname and a number but there can be the same number appearing multiple times.
I need to sort the numbers, and print the results, i.e. sort by the num of each struct and the print the name, sur and the num in each line starting from the smallest num going down to the largest. I don't know how to do this, please help me.
First, in C++ you don't need to repeat "struct" every time.
You can use the std::sort() function and give it a custom predicate.
bool db_cmp(const db &left, const db &right)
{
return left.num < right.num;
}
//...
db a[10];
std::sort(a, a + 10, db_cmp);
// then, display the contents of a..
You can use qsort.
int db_comparator ( const void * elem1, const void * elem2 )
{
struct db *first = (struct db *) elem1, *second = (struct db *) elem2;
return first->num - second->num;
}
qsort(a, sizeof(a)/sizeof(a[0]), sizeof(a[0]), db_comparator);
Do you have to implement your own sorting algorithm?
If so I strongly suggest typing "Sorting Algorithms" into google and/or checking out the wikipedia page.
Bubble sort is REALLY easy to implement (But is a poor sorting algorithm). It will come down to checking each number against each other and then swapping them is one is less than the other. String sorting is easy too as strcmp returns you a value less than 0 for a string "less" than the one being compared to and greater than zero for one "greater" than.
If you don't need to implement your own algorithm then use std::sort.
You could add a comparison method to your structure:
struct db
{
string name;
string sur;
int num;
bool operator <(const db& other)
{
if (name == other.name)
{
return sur < other.sur;
}
return name < other.name;
};
Now you can use the std::sort algorithm because your object has the '<' operator defined.