How to compare a vector of const char[] - c++

I have a vector that is composed of const char[] and I want to be able to check if each character array is equal to the rest in the vector. So if this is how I initialize, and then I assign a random const char[] to 5 parts of the vector array, how can I compare them without using ==?
const char sled[]="sled";
const char car[]="car";
const char house[]="house";
const char dog[] ="dog";
vector<const char[]> vect;
if (vect[0]== vect[1]== vect[2]. == vect[3] == vect[4])
{
cout << "They are all equal!"
return;
}

const char * or C-strings must be compared with strcmp(). You can write your own for loop that compares the chars one by one as well.

Your attempt to use C-style strings with std::vector won't even compile, but assuming you find a way to make it work, here's how I would compare two strings, making sure that no operator == is ever used (considering that strcmp might make use of that operator in its implementation):
bool eq(char a, char b) {
return !(static_cast<int>(a) - static_cast<int>(b));
}
bool are_equal(const char* a, const char* b) {
int i = 0;
for (; !eq(a[i], '\0'); ++i)
if (!eq(a[i], b[i]))
return false;
return (eq(b[i], '\0'));
}
And here's a live example of it's execution. Now, if you wish to actually make things the right way, here's how you would do it:
std::vector<std::string> vect;
vect.emplace_back("sled");
vect.emplace_back("car");
vect.emplace_back("house");
vect.emplace_back("dog");
if (std::all_of(
vect.begin() + 1,
vect.end(),
[&](const std::string& s) {
return s == vect.front();
}
)) {
std::cout << "They are all equal!";
}
And here's a live example of that too.
Have a nice coding day.

Related

Sorting vector of string pointers

I'm sorry if this has already been asked somewhere, but I haven't been able to find an answer to what I'm looking for.
I have a vector of std::string pointers, that I want to sort in alphabetical order, and I haven't been able to figure out how to do this. I am using std::sort.
I wrote up a quick program to test out what I was trying to do (since in the actual implementation, my code is being run in a child process, so it's kind of hard to debug):
#include <string>
#include <algorithm>
#include <vector>
#include <string.h>
bool cmpStrPtrs(std::string *a, std::string *b) {
std::string a1 = *a;
std::string a2 = *b;
if(a1 == a2) return 0;
return a1 > a2 ? 1 : -1;
}
int main(int argc, char *argv[]) {
std::vector<std::string *> vec;
std::string *str1 = new std::string("AAAAA");
std::string *str2 = new std::string("aaaaa");
std::string *str3 = new std::string("xxxxx");
std::string *str4 = new std::string("bfuen");
std::string *str5 = new std::string("xylophone");
vec.push_back(str1);
vec.push_back(str2);
vec.push_back(str3);
vec.push_back(str4);
vec.push_back(str5);
std::sort(vec.begin(), vec.end(), cmpStrPtrs);
for(std::string *str : vec) {
printf("%s\n", str->c_str());
}
return 0;
}
When I run this, I get this output:
$ ./strsort
xylophone
bfuen
xxxxx
aaaaa
AAAAA
This doesn't seem to be in any sort of alphabetical order at all, so I can assume that I'm either using sort() wrong or there's something wrong with my comparator function. I also tried it without a comparator function and I think that's just ordering them based on their memory locations from least to greatest, which doesn't actually change anything. I also tried using
bool cmpStrPtrs(std::string *a, std::string *b) {
return a->compare(*b);
}
but it gave me the same result.
If it's relevant, I'm compiling with g++ using the c++17 standard.
std::string::compare returns an int, not a bool. According to cppreference.com the return value is
negative value if *this appears before the character sequence specified by the arguments, in lexicographical order
zero if both character sequences compare equivalent
positive value if *this appears after the character sequence specified by the arguments, in lexicographical order strong text
The returned value is cast to bool which evaluates to true for all non-zero values. That means that your function returns true for every non-identical pair of strings.
The C++ standard actually defines operator< for strings so you can change your function to
bool cmpStrPtrs(std::string *a, std::string *b) {
return *a < *b;
}
But that still leaves a big issue in your code. You absolutely do not need pointers for this. In fact, you are leaking memory right now because you neglect to delete them. The proper tool for this job is std::vector<std::string>. This has the added benefit that without the extra level of indirection, std::sort can implicitly call operator< without a helper function, leading to the following solution.
std::vector<std::string> vec;
vec.emplace_back("AAAAA");
vec.emplace_back("aaaaa");
vec.emplace_back("xxxxx");
vec.emplace_back("bfuen");
vec.emplace_back("xylophone");
std::sort(vec.begin(), vec.end());
You can do it with a lambda:
std::sort(vec.begin(), vec.end(), [](std::string * a, std::string * b) {
return *a < *b;
});
Your comparison function is meant to simulate the less-than operator - that means it should return true if a comes before b. Your current implementation returns true if a doesn't equal b.
You have:
if(a1 == a2) return 0;
return a1 > a2 ? 1 : -1;
which should be:
if(a1 == a2) return false;
return a1 > a2 ? false : true;
or just:
return a1 < a2;
std::sort expects Strict Weak Ordering. It doesn't give a crap about equals; it only cares about before and after.
The comparison function should return true if the right hand side goes before left hand side. Unfortunately in
bool cmpStrPtrs(std::string *a, std::string *b) {
std::string a1 = *a;
std::string a2 = *b;
if(a1 == a2) return 0;
return a1 > a2 ? 1 : -1;
}
bool is true for any value that is not 0. This means that both greater than and less than are true. This makes logical ordering pretty much impossible because greater than and less than go before.
Improvement cut 1: return bool based on lexicographic (alphabetical) ordering. String already implements a less than operator that does exactly what you want. Let's use it.
bool cmpStrPtrs(std::string *a, std::string *b) {
std::string a1 = *a;
std::string a2 = *b;
return a1 < a2;
}
Improvement cut 2: std::string a1 = *a; creates a brand new string that is a copy of the original. Since you have a pointer to the original you can dereference the pointer and use the original. No need for the copy.
bool cmpStrPtrs(std::string *a, std::string *b) {
return *a < *b;
}

Map operator < conditions - invalid comparator

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.

Using std::sort to sort an array of C strings

I'm trying to use std::sort to sort an array of C strings. Here's what I've done:
const char *tab[5];
//then fill up the tab
sort(tab, tab+5);
This doesn't seem to work. I've also tried using sort(tab, tab+5, strcmp), which worked, but when I put it into a function, the array remains unchanged. Sorting function:
void sortfunc (const char *tab[], int n){
filltab(tab, n); //n is the ammount of strings
sort(tab, tab+n, strcmp);
}
What's wrong with my code?
std::sort: The comparison object expected by it has to return ​true if the first argument is less than (i.e. is ordered before) the second.
strcmp, the function you provided has another return convention:
Negative value if lhs is less than rhs.
​0​ if lhs is equal to rhs.
Positive value if lhs is greater than rhs.
(here lhs and rhs reffer to left hand operator and right hand operator)
So you have to create your own wrapper function around strcmp.
If you could use c++11 and if you could use c++ for real (i.e. std::string and std::vector) you can use other cool tricks like lambdas.
The classic solution:
bool cmp(char const *lhs, char const *rhs) {
return strcmp(lhs, rhs) < 0;
}
std::sort(tab, tab + n, cmp);
The lambda solution:
std::sort(tab, tab + n, [](char const *lhs,
char const *rhs) { return strcmp(lhs, rhs) < 0; });
The real C++ solution
std::vector<std::string> v = ...;
std::sort(std::begin(v), std::end(b));
I've got it working. Like #bolov and #Svalorzen said, I had to write a bool function that returns true or false, instead of -1, 0, 1, like strcmp() does. Here it is, if anyone needs it in the future:
bool cmp(const char *str1, const char *str2){
if(strcmp(str1, str2)<0) return true;
else return false;
}
sort(tab, tab+5, cmp);
Thanks for your help.

Visual C++ - qSort with Strings

Yes, I want to use the qsort() function with two strings, most likely those will be tested as character arrays. When I run it,I get an unhandled exception in qsort.c at the line(151):
if (__COMPARE(context, lo, mid) > 0) {
swap(lo, mid, width);
}
This is my code:
#include <iostream>
#include <iomanip>
using namespace std;
struct SaleSlip{
char name[20];
int ProdID;
double value;
};
int compare(void const *a, void const *b);
ostream& operator<<(ostream& out, SaleSlip& sales);
int main(){
SaleSlip sales[17] = {
{"Eric", 1, 200000.00},
{"Sookie", 2, 200.00},
{"Sookie", 4, 200.50},
{"Bill", 3, 5000.00},
{"Bill", 5, 7500.00},
{"Tara", 4, 350.50},
{"Eric", 2, 200.00},
{"Tara", 2, 200.00},
{"Tara", 4, 350.50},
{"Bill", 5, 2500.00},
{"Sookie", 1, 50000.00},
{"Sookie", 2, 200.00},
{"Eric", 5, 10000.00},
{"Tara", 2, 200.00},
{"Tara", 4, 150.50},
{"Bill", 5, 1000.00},
{"Sookie", 4, 400.50}
};
cout << "The array before sorting is: " << endl;
for(int i = 0; i < 17; i++)
cout << sales[i];
qsort(sales[0].name, 17, (sizeof(sales)/sizeof(char*)), compare);
cout << "The array after sorting is: ";
system("pause");
return 0;
}
ostream& operator<<(ostream& out, SaleSlip& sales){
out << setiosflags(ios::left | ios::fixed) << setw(7) << sales.name << setw(3) << sales.ProdID
<< setprecision(2) << sales.value << endl;
return out;
}
int compare(void const *a, void const *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
I am testing in compare properly? Am I calling qsort properly?
You probably want your compare function to take SaleSlip pointers as the argument. In the compare function do strcmp(a->name,b->name). Of course your arguments to qsort will change to become SaleSlip structs.
What you are doing wrong is:
I. You pass in 17 as both the element size and the element count to qsort(). That's wrong, the 2nd argument is the number of elements in the array, the 3rd one is the size of one individual element.
II. you want to sort the array, but you don't pass in the address of its first element, but a pointer to the name member of the first element. Take this and the incorrect element size, and from now on, all the pointers qsort() operates on are quite arbitrary, and using them is not any good. What you could do is:
I. Write a proper comparator function and don't try to include nasty tricks:
qsort(sales, sizeof(sales) / sizeof(sales[0]), sizeof(sales[0]), comp);
int comp(const void *a, const void *b)
{
// the two lines below are the aesthetic reason
// for NOT using qsort() in C++. The ugly cast is not needed in C.
const SaleSlip *s1 = static_cast<const SaleSlip *>(a);
const SaleSlip *s2 = static_cast<const SaleSlip *>(b);
return strcmp(s1->name, s2->name);
}
II. Or, even better: use std::sort, std::string and define SaleSlip::operator <:
class SaleSlip {
bool operator <(const SaleSlip &that) {
return this->name < that.name;
}
};
std::sort(sales, sales + sizeof(sales) / sizeof(sales[0]));
Bonus: don't use hard-coded sizes and types. 17 is dangerous, sizeof(array) / sizeof(SaleSlip) is better but still not perfect, sizeof(array) / sizeof(array[0]) is entirely safe, no matter what you do with the base type and the element count of the array.
Three problems:
The first argument should just be sales - the array you want to sort.
The size argument should be sizeof(SaleSlip) - the size of each element.
The comparison is doing something very strange. To get a pointer to the name, you want static_cast<const SaleSlip*>(a)->name (or (const char*)a if you like to live dangerously).
These problems can be avoided by using the C++ std::sort function (and std::string for the string), which is type-safe and (often) more efficient than qsort. Then you simply do
std::sort(std::begin(sales), std::end(sales),
[](SaleSlip const & a, SaleSlip const & b) {return a.name < b.name;});
Use std::sort : qsort is harder to use in almost every case, and less efficient.
First, write a order function:
struct SaleSlip{
char name[20];
int ProdID;
double value;
};
bool order( SaleSlip const& lhs, SaleSlip const& rhs ) {
return strncmp(lhs.name, rhs.name, sizeof(lhs.name))<0;
}
then feed this to std::sort:
std::sort( &sales[0], &sales[sizeof(sales)/sizeof(sales[0])], order );
and your are done.
In C++11, that line is better:
std::sort( std::begin(sales), std::end(sales), order );
You can also replace name with a std::string rather than work with a fixed sized buffer, but I understand there are reasons to work with raw char buffers. If you do so, simply change order to return lhs.name < rhs.name.
The problems in your original code, above and beyond the choice to use qsort, is that the blocks of memory you are sorting are SaleSlip objects, not char* objects. First, fix your compare function (and give it a better name):
int compare_pvoid_SaleSlips(void const *a, void const *b) {
SalesSlip const* lhs = static_cast<SalesSlip const*>(a);
SalesSlip const* rhs = static_cast<SalesSlip const*>(b);
return strncmp(lhs->name, rhs->name, sizeof(lhs->name)/sizeof(lhs->name[0]));
}
Next, fix your call to qsort:
qsort(&sales[0], 17, (sizeof(sales)/sizeof(sales[0])), compare_pvoid_SaleSlips);
which should work. However, following this method will be slower, more error prone, more brittle, and in every way worse in a C++ program than the std::sort solution.

How to compare C++ string using qsort in c?

I tried to learn the qsort function of the c-library stdlib. This is provided even in c++. But i dont understand how to use them for sorting c++ strings. I am not sure of what the parameters should be for the sizeof() operator and whether my compare_str code is right. I tried this code:
#include<iostream>
#include<cstdlib>
using namespace std;
#include<string>
int compare_str( const void *a, const void *b){
string obj = (const char*)a;
string obj1 = (const char*)b;
return obj.compare(obj1);
}
int main(){
string obj[4] = {"fine", "ppoq", "tri", "get"};
qsort(obj, 4, sizeof(obj[0].length()), compare_str);
for( int i=0; i<4; i++)
cout<<obj[i]<<endl;
return 0;
}
My output was:
ppoq
tri
get
fine
I am not able to make out the error. Please help.
You cannot and must not use qsort on an array of std::strings. The elements must be of trivial type, which strings are not, and thus the behaviour is undefined. From 25.5/4 ("qsort"):
The behavior is undefined unless the objects in the array pointed to by base are of trivial type.
The reason is that qsort will memcpy the array elements around, which is not possible for C++ objects in general (unless they're sufficiently trivial).
If you do have a trivial type, you can use this generic qsorter-comparator (but of course this is a terrible idea, and the inlined std::sort is always preferable):
template <typename T>
int qsort_comp(void const * pa, void const * pb)
{
static_assert<std::is_trivial<T>::value, "Can only use qsort with trivial type!");
T const & a = *static_cast<T const *>(pa);
T const & b = *static_cast<T const *>(pb);
if (a < b) { return -1; }
if (b < a) { return +1; }
return 0;
}
Use: T arr[N]; qsort(arr, N, sizeof *arr, qsort_comp<T>);
Don't use this. Use std::sort instead.
Better be C++ oriented and use std::sort for your array:
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
int main() {
std::string obj[4] = {"fine", "ppoq", "tri", "get"};
std::sort(obj, obj + 4);
std::copy(obj, obj + 4, std::ostream_iterator<std::string>(std::cout, "\n"));
}
AFAIK - std::sort uses quick sort.
[UPDATE] See comments, std::sort is not always pure quick sort.
[UPDATE2]
If you want to learn qsort - change std::string to const char* and define function based on strcmp. Remember that qsort passes pointers to elements in an array - so dereference const void* to get const char*. See:
#include <stdlib.h>
#include <string.h>
int compare_cstr(const void* c1, const void* c2)
{
return strcmp(*(const char**)(c1), *(const char**)(c2));
}
int main() {
const char* obj[4] = {"fine", "ppoq", "tri", "get"};
qsort(obj, 4, sizeof(obj[0]), compare_cstr);
std::copy(obj, obj + 4, std::ostream_iterator<const char*>(std::cout, "\n"));
}
The problem is that you give qsort an array of C++ strings. In your comparison function, you seem to except C strings, since you cast them to (const char*).
Also, the third parameter of qsort, the size of data, you actually give wrong value. sizeof(obj[0].length()) will result in sizeof(size_t), which is obviously wrong. sizeof(obj[0]) would be more correct, but remember that qsort won't call copy constructor of string, which might lead to problems.
I would suggest not to use qsort with C++ strings.
See answer provided by PiotrNycz for correct solution.
You should use the std::sort template function provided by the C++ standard library (in the <algorithm> header file). By default, std::sort uses the less than comparison operator to order elements (std::string already implements operator<). If you need to specify an ordering condition (for example, case insensitive string compare), std::sort allows you to specify an ordering function object.
Example:
#include <string>
#include <algorithm>
bool caseInsensitiveOrdering(const std::string& lhs, const std::string& rhs)
{
// return true if lowercase lhs is less than lowercase rhs
}
int main()
{
std::string names[] = {"chuck", "amy", "bob", "donna"};
size_t nameCount = sizeof(names) / sizeof(names[0]);
// Sort using built-in operator<
std::sort(names, names + nameCount);
// Sort using comparison function
std::sort(names, names + nameCount, &caseInsensitiveOrdering);
}
Your error is in the declaration of the size in qsort. What is expected is the size of a member, which is, in your case, a string. So you want to use:
qsort(obj, 4, sizeof(string), compare_str);
However, you need to work with pointer to string, rather than strings themselves. Then, the code should look like:
int compare_str( const void *a, const void *b){
const string* obj = (const string*)a;
const string* obj1 = (const string*)b;
return obj->compare(*obj1);
}
// ...
string* obj[4] = { new string("fine"), new string("ppoq"),
new string("tri"), new string("get") };
qsort(obj, 4, sizeof(string*), compare_str);
// And delete the objects
for(int i = 0 ; i < 4 ; ++i) delete obj[i];
Works for me:
#include<iostream>
#include<cstdlib>
using namespace std;
#include<string>
int compare_str( const void *a, const void *b){
string* obj = (string*)a;
string* obj1 = (string*)b;
return obj->compare(*obj1);
}
int main(){
string obj[4] = {"fine", "ppoq", "tri", "get"};
qsort(obj, 4, sizeof(string), compare_str);
for( int i=0; i<4; i++)
cout<<obj[i]<<endl;
return 0;
}