How to sort vector of class contain CString? - c++

I declared a class like this:
class myclass{
array myarr;
pair<PT3D,PT3D> mypair;
ID myid;
CString tag;
}
after fill data, i have a vector<myclass> myvec;
How can I sort this vector based on the CString value of each class?
bool SortCompare(const wchar_t* a, const wchar_t* b)
{
if (wcslen(a) == wcslen(b))
{
int k = (int)wcslen(a);
return std::wcsncmp(a, b, k);
}
else
return wcslen(a) < wcslen(b);
}
sort(myvec.begin(), myvec.end(), [h](myclass& a,
myclass& b) {
return SortCompare(a.tag, b.tag);
});
However this doesn't work properly , my desired result is a list sorted by human sort as this post.
but I don't know how to use CSortStringArray::CompareAndSwap function, how to use this code with std::sort?
How can I correct my codes?

Note CString provides less operator so you do not have to fall back to C-API like wcsncmp.
You are making this overcomplicated.
using C++11:
std::sort(myvec.begin(), myvec.end(), [](const myclass& a, const myclass& b) {
return a.tag < b.tag;
});
C++20 has even something simpler called projection:
std::ranges::sort(myvec, {}, &myclass::tag);
Now the issue of alphanumeric compare. This can look like this:
struct AlphanumericSplitResult {
std::string_view prefix;
std::string_view digits;
std::string_view suffix;
};
AlphanumericSplitResult alphanumericSplit(std::string_view s)
{
auto i = s.find_first_of("0123456789"sv);
i = i == std::string_view::npos ? s.size() : i;
auto j = s.find_first_not_of("0123456789"sv, i);
j = j == std::string_view::npos ? s.size() : j;
return { s.substr(0, i), s.substr(i, j - i), s.substr(j) };
}
bool asNumberLess(std::string_view l, std::string_view r)
{
return l.size() != r.size() ? l.size() < r.size() : l < r;
}
bool alphanumeric_less(std::string_view l, std::string_view r)
{
if (l.empty())
return !r.empty();
auto a = alphanumericSplit(l);
auto b = alphanumericSplit(r);
if (a.prefix != b.prefix)
return a.prefix < b.prefix;
if (a.digits != b.digits)
return asNumberLess(a.digits, b.digits);
return alphanumeric_less(a.suffix, b.suffix);
}
It passes all test I wrote: https://godbolt.org/z/hdG6fq9sa
and it is easy to feed to both algorithms.
Disclaimer: there is small bug in implementation - can you find it? Depending on requirements there are more small issues.

std::wcsncmp returns 0, > 0 or < 0. > 0 and < 0 are converted to true, 0 to false. Thus, compare "a" with "b" and "b" with "a" both return true. std::sort expects a proper compare function. Perhaps you want return std::wcsncmp(a, b, k) < 0.

In your else, you return Len(a) < Len( b) but this is not the same as Len(a) != Len(b). I would just return false in the else block since you know they aren't the same at that point.

Related

C++ how to use std::adjacent and std::count_if together, to count the number of occurrences of a condition in a vector

In C++, I have a class member function that takes a vector of objects.
From each two consecutive objects, I create a std::pair<unsigned, unsigned> (Level1, Level2).
What I would like to do is count the number of times that that the pair for A is greater than B in the vector.
I tried to this this with std::count_if but it does not accept 2 args in the predicate.
So I started t use std::adjacent_find.
How could I extend what I have to actually count the the number of time that the pair for A is greater than B?
int mv::class::countOccurances(std::vector<OpListIterator>& sortedops)
{
std::adjacent_find(sortedops.begin(), sortedops.end(),
[](opListIterator a, opListIterator b){
std::pair<unsigned,unsigned> A (a->get<unsigned>("Level1"),a->get<unsigned>("Level2"));
std::pair<unsigned,unsigned> B (b->get<unsigned>("Level1"),b->get<unsigned>("Level2"));
return A > B;
});
}
ok as i understand here problem is in not defined less operator, so here is sample solution.
int mv::class::countOccurances(const std::vector<OpListIterator>& sortedops)
{
std::count_if(sortedops.begin(), sortedops.end(),
[](const opListIterator& a, const opListIterator& b){
std::pair<unsigned,unsigned> A (a->get<unsigned>("Level1"),a->get<unsigned>("Level2"));
std::pair<unsigned,unsigned> B (b->get<unsigned>("Level1"),b->get<unsigned>("Level2"));
return ( A.first == B.first ? A.second < B.second : A.first < B.first);
});
}
First to simplify, let introduce the projection function:
std::pair<unsigned, unsigned> projection(opListIterator it)
{
return { it->get<unsigned>("Level1"), it->get<unsigned>("Level2")};
}
You might abuse of std::adjacent_find with that strange predicate:
int count = 0;
std::adjacent_find(sortedops.begin(), sortedops.end(),
[&](opListIterator current, opListIterator next) {
count += projection(current) > projection(next);
return false; // never considered as equal
});
return count;
A proper way to use would be:
int count = 0;
auto it = sortedops.begin();
while (it != sortedops.end()) {
it = std::adjacent_find(it, sortedops.end(),
[&](opListIterator current, opListIterator next) {
return projection(current) > projection(next);
});
if (it != sortedops.end()) { ++count; ++it; }
}
return count;
For std::count_if, ranges (ranges-v3 or C++20) might help:
auto range = ranges::view::zip(sortedops, sortedops | ranges::view::drop(1)); // current/next pairs
return std::count_if(begin(range), end(range), [](const auto& p){
return projection(p.first) > projection(p.second);
});

How to erase element from std::vector<string> by its length(erase not working)

i have given a vector `
vector<string> inputArray = { "aba","aa","ad","vcd","aba" };
and i want to return this vector which contains only string with the longest length, in this case i want to return only {"aba","vcd","aba"}, so for now i want to erase elements which length is not equal to the highest `
vector<string> allLongestStrings(vector<string> inputArray) {
int length = inputArray.size();
int longstring = inputArray[0].length();
int count = 0;
vector<string> result;
for (int i = 0; i < length; i++)
{
if (longstring < inputArray[i].length())
{
longstring = inputArray[i].length();
}
count++;
}
for (int = 0; i<count;i++)
{
if (inputArray[i].length() != longstring)
{
inputArray[i].erase(inputArray.begin() + i);
count--;
i--;
}
}
return inputArray;
}
but i get this error no instance of overloaded fucntion "std::basic_string<_Elem,_Traits,_Alloc>::erase[with_Elem=char,_Traits=std::char_traits<char>,_Alloc=std::allocator<char>]" matches the argument list" in inputArray[i].erase(inputArray.begin()+i); this line
what's wrong?
There are other problems, but this specific compiler message is telling you that's not the right way to remove specific character(s) from a string.
However, reading the question in the OP, we see that you wanted to remove a string from a vector. To fix that one specific error, simply change
inputArray[i].erase( /*character position(s) in the string*/ )
to
inputArray.erase( /*some position in the array*/ )
Or you could fix it so it uses an iterator in the string denoted by inputArray[i] to actually delete characters from that string, which of course isn't what you said you wanted to do. The point is, the error message is because you're using the wrong iterator type because you think that you're working with a vector, but you actually told it to work with a string that you got out of the vector.
And then you will compile and have other issues which are well covered in comments already.
The issue with inputArray[i].erase(inputArray.begin() + i); can be fixed as shown in Kenny Ostrom's answer.
I'd like to point out that the OP could make use of the erase-remove idiom or even create a new vector with only the bigger strings instead (the posted code is already copying the source vector).
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
template <typename InputIt>
auto only_the_longest_of(InputIt first, InputIt last)
{
using value_type = typename std::iterator_traits<InputIt>::value_type;
std::vector<value_type> result;
// find the longest size
auto longest = std::max_element(first, last,
[](value_type const &a, value_type const &b) {
return a.size() < b.size();
});
if ( longest == last )
return result;
// extract only the longest ones, instead of erasing
std::copy_if( first, last, std::back_inserter(result)
, [max_size = longest->size()] (value_type const& v) {
return v.size() >= max_size;
});
return result;
}
template <typename T>
auto erase_the_shortest_from(std::vector<T> &input)
{
// find the longest size
auto longest = std::max_element(input.cbegin(), input.cend(),
[](T const &a, T const &b) {
return a.size() < b.size();
});
if ( longest == input.cend() || longest->size() == 0 )
return input.end();
// implement erase-remove idiom
return input.erase(std::remove_if(
input.begin(), input.end(), [max_size = longest->size()] (T const &v) {
return v.size() < max_size;
}));
}
int main()
{
std::vector<std::string> test = {
"aba", "aa", "ad", "vcd", "aba"
};
// The original vector remain unchanged
auto result = only_the_longest_of(test.cbegin(), test.cend());
for (auto const& str : result)
std::cout << str << '\n';
std::cout << '\n';
// This will change the vector
erase_the_shortest_from(test);
for (auto const& str : test)
std::cout << str << '\n';
}

Sort structure array in alphabetical order

I have a structure array (A[#]) named Sheep (since my task is about sheeps DNR). After I do whatever the task asked I am left with this struct :
struct Sheep
{
string Vardas;
char Fragmentas[CMax];
int atitikme = 0;
};
and inside my it the data is:
(string Vardas) | (char Fragmentas[CMax]) | (int atitikme)
Baltukas TAGCTT 3
Bailioji ATGCAA 3
Smarkuolis AATGAA 1
(char Fragmentas[CMax] won't be using so u don't have to look at it, I only named it to make it clear).
ALL of this data comes from U2.txt file and cant be manually typed in a code.
All its left to do is to sort it by these rules:
It goes from bigger to smaller by 'int atitikme'.
IF 'int atitikme' is equal then it will have to sort by 'A[#].Vardas in a in alphabetical order.
To sort it by 'int atitikme' I created a code:
string q;
char w[20];
int e;
for (int o = 0; o < n-1; o++)
{
for (int p = o+1; p < n-1; p++)
{
if (A[p].atitikme > A[o].atitikme)
{
// - Vardo Keitimas
q = A[o].Vardas;
A[o].Vardas = A[p].Vardas;
A[p].Vardas = q;
// - A[#].atitikme keitimas
e = A[o].atitikme;
A[o].atitikme = A[p].atitikme;
A[p].atitikme = e;
// - DNR farkmentu keitimas
for (int r = 0; r < m; r++)
{
w[r] = A[o].Fragmentas[r];
A[o].Fragmentas[r] = A[p].Fragmentas[r];
A[p].Fragmentas[r] = w[r];
}
}
}
}
n = 4 | m = 6
How/what do i need to add to this code to make it go:
else if (A[p].atitikme == A[o].atitikme)
{
<code>
}
That if 'atitikme' is == to another 'atitikme' then A[p].Vardas and A[o].Vardas has to be sorted in an alphabetical order. but only those 2 from the whole array.
OR if its too hard to understand what I meant, could anyone post a code, in the answer box, were it would sort in a alphabetical order between 2 string's?
NOTE:
the whole line data
(string Vardas) (char Fragmentas[CMax]) (int atitikme)
has to stay the same, only the place in the line has to be diffirent and sorted by those rules I mentioned before.
The output should be:
Bailioji 3
Baltukas 3
Smarkuolis 1
EDIT:
My current output is:
Baltukas 3
Bailioji 3
Smarkuolis 1
P.s. The task allows to use everything as-long as its C++ and does not have to create, or read, any other file.
Here I have used std::vector<> instead of array to store the sheeps.
Secondly, using std::sort() and a lambda function, you can easily mention how you want to sort the elements in the std::vector<>/ Sheeps. That would be the easiest way to approach.
Here is the live code, in case of reviewing: https://www.ideone.com/ay7TWU
#include <iostream>
#include <vector>
#include <algorithm>
struct Sheep
{
std::string Vardas;
std::vector<char> Fragmentas;
int atitikme;
};
int main()
{
std::vector<Sheep> vec =
{
{"Baltukas", {'T','A','G','C','T','T'}, 3},
{"Bailioji", {'A','T','G','C','A','A'}, 3},
{"Smarkuolis",{'A','A','T','G','A','A'}, 1},
{"Hmarkuolis",{'A','A','T','G','A','A'}, 1},
{"Kmarkuolis",{'A','A','T','G','A','A'}, 2}
};
std::sort(vec.begin(), vec.end(), [](const Sheep& lhs, const Sheep& rhs)
{
return (lhs.atitikme == rhs.atitikme) ?
lhs.Vardas < rhs.Vardas: // if atitikme's of sheeps are equal
lhs.atitikme > rhs.atitikme; // if atitikme's of sheeps are not equal
});
for (const auto& it: vec)
std::cout << it.Vardas << " " << it.atitikme << "\n";
return 0;
}
The output:
Bailioji 3
Baltukas 3
Kmarkuolis 2
Hmarkuolis 1
Smarkuolis 1
The best is to solve your problem one by one.
First - define the sorting order - see doc about - e.g. in std::less
So, you need functor class that defines your sorting order:
class SheepOrder
{
public:
bool operator() ( const Sheep& left, const Sheep& right) const
{
// It goes from bigger to smaller by 'int atitikme'.
if (left.atitikme > right.atitikme)
return true;
if (left.atitikme < right.atitikme)
return false;
//IF 'int atitikme' is equal then it will have to sort it in a in alphabetical order.
// I guess you meant Vardas
return left.Vardas < right.Vardas;
}
};
Now, having defined the order - just use std::sort - it can be used with arrays - no problem:
Sheep sheeps[100];
// ..
std::sort(std::begin(sheeps), std::end(sheeps), SheepOrder{});
or:
void sortSheeps(Sheep* array, std::size_t numOFSheeps)
{
std::sort(array, array + numOfSheeps, SheepOrder{});
}
You can also use std::tuple to make it easier to define sorting order (tuple has operator < by default if their elements have this operator too):
class SheepOrder
{
public:
bool operator() ( const Sheep& left, const Sheep& right) const
{
return tieMembersForSorting(left) < tieMembersForSorting(right);
}
private:
static auto tieMembersForSorting( const Sheep& object)
{
return std::make_tuple(-object.atitikme, // - to revert order
std::ref(object.Vardas)); // ref - to not make copy of string
}
};
With tieMembersForSorting defined as free function - lambda could be used as well (as it will be just one liner):
inline auto tieMembersForSorting( const Sheep& object)
{
return std::make_tuple(-object.atitikme, // - to revert order
std::ref(object.Vardas)); // ref - to not make copy of string
}
std::sort(begin(...), end(...), [](Sheep const& left, Sheep const& right)
{ return tieMembersForSorting(left) < tieMembersForSorting(right); });
https://en.cppreference.com/w/cpp/algorithm/sort shows you how to use std::sort.
You write a function bool less_than(const Sheep& a, const Sheep& b) that represents the order of two sheep and then simply call std::sort(container.begin(), container.end(), less_than);, with container being something like a vector of Sheep.
Edit: The function written out:
bool less_than(const Sheep& a, const Sheep& b)
{
if(a.atitikme != b.atitikme) return a.atitikme < b.atitikme;
return a.Vardas < b.Vardas;
}

C++: Equality of struct of strings

In my code I have a struct, similar to the following:
struct basket {
std::string a;
std::string b;
std::string c;
std::string d;
};
I want to be able to compare two structs and determine > 0, < 0, == 0 by comparing the concatenation of all of those strings
std::string total = (a+b+c+d);
However, I want to achieve this without doing the actual concatenation, because this comparison is used many times, and it ends up being a run-time bottleneck. I know that if that's the case, I should look into avoiding the use of strings, but for now I'd simply like to do this comparison easily without concatenating.
Right now, I use a giant if statement. For example compare string a from each instance of the struct, if they're the same, compare b, if they're the same, compare c, if they're the same then finally compare d, but I was wondering if there's a cleaner way to do this in c++ that doesn't have the runtime hit of concatenations.
Thank you.
If I understand you correctly, you want two structs to be equal if the concatenation of strings is equal, so
a == "hello", b == "there", ...
matches
a = "hel", b == "lothere", ...
I would do this using boost::range::join:
struct basket {
...
bool operator==(const basket& other) const
{
using namespace boost::range;
auto left = join(join(join(a, b), c), d);
auto right = join(join(join(other.a, other.b), other.c), other.d);
return equal(left, right); // http://www.boost.org/doc/libs/1_51_0/libs/range/doc/html/range/reference/algorithms/non_mutating/equal.html
}
};
boost::range::join (http://www.boost.org/doc/libs/1_57_0/libs/range/doc/html/range/reference/utilities/join.html) creates a joined range without doing the concatenation. It internally just iterates all the way across. The only additional overhead is checking for the end of the first range and moving to the second range, so it should be much faster than actually concatenating.
UPDATE:
I originally missed the desire for a strcmp style return. Unfortunately I can't find any standard algorithms that return a value like this. On the upside, though, it's easy enough to write. Here is the update version, with compare instead of equal:
#include <boost/range/join.hpp>
template <typename SinglePassRange1, typename SinglePassRange2>
int compare(const SinglePassRange1& left, const SinglePassRange2& right)
{
using namespace std;
auto leftIt = begin(left);
auto leftEnd = end(right);
auto rightIt = begin(right);
auto rightEnd = end(right);
for ( ; leftIt != leftEnd
&& rightIt != rightEnd
&& *leftIt == *rightIt
; ++leftIt, ++rightIt)
{
}
// should be safe since one-past-end for strings is '\0'
return static_cast<int>(*leftIt) - static_cast<int>(*rightIt);
}
struct basket {
std::string a;
std::string b;
std::string c;
std::string d;
int compare(const basket& other) const
{
using namespace boost::range;
auto left = join(join(join(a, b), c), d);
auto right = join(join(join(other.a, other.b), other.c), other.d);
return ::compare(left, right);
}
};
Tested on GCC 4.9.1 Ubuntu.
That compare free function should probably be called compareStringRanges or something since that implementation is only valid for strings. I'll leave that to the realm of individual aesthetics.
The easy way to work with these fields would be to use an array for fields' storage, rather than individual fields.
You can create a temporary array to simplify your code. Your implementation will almost surely put the std::array's data on the stack.
Illustration:
int compare(const basket &pOther) const {
const BasketStringRefs a(this->allStrings());
const BasketStringRefs b(pOther.allStrings());
// ...your evaluation here, involving iteration over a and b...
}
private:
typedef std::array<const std::string*, 4> BasketStringRefs;
BasketStringRefs allStrings() const {
return BasketStringRefs{{&a, &b, &c, &d}};
}
It'll be trouble to set up, but it'll be efficient: store a, b, c, and d in a single buffer. You can maintain pointers to each to keep track of where each begins (or if you know a max size, store them at relative locations 0, max size, max size*2, and max size+3).
When you compare baskets, compare their buffers, using a for loop that goes up to buffer size rather than a while that ends with the null character. When you get to a null character, you're done with that string; go on to the next one to see the next char you want to compare. If that happens 4 times, you've read all 4, and should quit that basket.
//baskets are a and b
for (int aIndex = 0, bindex = 0; aIndex < MAX && bIndex < MAX; ++aIndex, ++bIndex)
{
//if we reach null char for basket a, skip to next string in basket a. Same for b.
//if we run out of strings in basket a first, it's shorter
// and comes first in alpha order. Same for b.
//if we run out of strings in both baskets without finding any differences,
// return 0 for equal
//compare the next char in each buffer; if different, return -1 or 1 appropriately
}
I wrote a quick and dirty iterator for the concatenated string, just for fun:
#include "stdafx.h"
#include <iostream>
#include <assert.h>
class concatenation {
private:
std::string a, b, c, d;
public:
concatenation(){}
concatenation(const std::string &s1, const std::string &s2, const std::string &s3, const std::string &s4)
: a(s1), b(s2), c(s3), d(s4)
{}
class const_iterator {
private:
size_t stringnumber, stringposition;
const concatenation &target;
public:
const_iterator(const concatenation &r, size_t n, size_t p) : target(r), stringnumber(n), stringposition(p) {}
bool operator ==(const const_iterator &rhs){ return stringnumber == rhs.stringnumber && stringposition == rhs.stringposition; }
bool operator !=(const const_iterator &rhs){ return !(*this == rhs); }
char operator *(){
switch (stringnumber){
case 0: return target.a[stringposition];
case 1: return target.b[stringposition];
case 2: return target.c[stringposition];
case 3: return target.d[stringposition];
default:
return '\0';
}
}
const_iterator& operator ++(){
size_t current_length;
switch (stringnumber){
case 0: current_length = target.a.size(); break;
case 1: current_length = target.b.size(); break;
case 2: current_length = target.c.size(); break;
case 3: current_length = target.d.size(); break;
default:
assert(0);
current_length = 0;
}
if (stringposition < current_length-1){
++stringposition;
} else {
stringposition = 0;
++stringnumber;
}
return *this;
}
};
const_iterator begin() const {
return const_iterator(*this, 0, 0);
}
const_iterator end() const {
return const_iterator(*this, 4, 0);
}
};
int compare(const concatenation &left, const concatenation &right){
concatenation::const_iterator p1 (left.begin());
concatenation::const_iterator p2 (right.begin());
while (p1 != left.end() && p2 != right.end() && (*p1)==(*p2)){
++p1;
++p2;
}
if (*p1 != *p2){
if (p1 == left.end() && p2 != right.end())
return -1;
if (p1 != left.end() && p2 == right.end())
return 1;
if (*p1 < *p2)
return -1;
else
return 1;
}
return 0;
}
int main()
{
concatenation test("hel", "lo ", "wor", "ld.");
for (concatenation::const_iterator pos = test.begin(); pos != test.end(); ++pos)
std::cout << *pos;
std::cout << std::endl;
{
// same
concatenation first("hello", " ", "world", ".");
concatenation second("hel", "lo ", "wor", "ld.");
if (0 != compare(first, second))
assert(0);
}
{
// first character different
concatenation first("hello", " ", "world", ".");
concatenation second("jel", "lo ", "wor", "ld.");
if (-1 != compare(first, second))
assert(0);
if (1 != compare(second, first))
assert(0);
}
{
// middle character different
concatenation first("hello", " ", "world", ".");
concatenation second("hel", "p! ", "wor", "ld.");
if (-1 != compare(first, second))
assert(0);
if (1 != compare(second, first))
assert(0);
}
{
// length different
concatenation first("hello", "", "", "");
concatenation second("hel", "lo ", "wor", "ld.");
if (-1 != compare(first, second))
assert(0);
if (1 != compare(second, first))
assert(0);
}
return 0;
}

Comparing arrays of objects with arrays of fields of objects

Is there a good way to compare arr[i].A to A[i] and arr[i].B to B?
int A[10], B[10];
class Foo {
int A, B;
};
Foo arr[10];
I could do the following:
for (i=0;i<10;i++) {
if (A[i] == arr[i].A) {}
if (B[i] == arr[i].B) {}
}
But, this is painful especially if there are a lot of fields, and the if() conditional does the same thing over and over, there will be a lot of code duplication. What I really want to do is parametrize this somehow and call a function like (test(A,arr)). I guess I can solve this by using #define macros, but that seems ugly.
Any suggestions?
Also I want to avoid creating a new array of Foo objects because I don't want to create new objects that may have many fields I don't care about, also I may want to compare different subsets of fields.
IF the ranges are of equal size you can use std::equal with a predicate (or a lambda):
bool CompA( int lhs, Foo rhs ){
return lhs == rhs.A;
};
...
// to check for equality
bool result = std::equal( A, A + 10, arr, CompA );
...
// to see where the mismatch is
std::pair< int*, Foo* > result = std::mismatch( A, A + 10, arr, CompA );
size_t index = result.first - A;
if( index < 10 ){
std::cout << "Mismatch at index " << index << " A = " << *result.first << " Foo.A = " << (*result.second).A << std::endl;
}
There are standard-library algorithms for doing operations on containers (including arrays, kinda) but using them typically produces code that's harder to read and maintain, and no shorter or more efficient, than straightforward loops.
However, it sounds as if you might want to know about pointers-to-members.
bool all_equal(int Foo::* member, const Foo * obj_array, const int * elem_array, size_t n) {
for (int i=0; i<n; ++i) {
if (obj_array[i].*member != elem_array[i]) return false;
}
return true;
}
...
if (all_equal(&Foo::A, arr, A, 10) && all_equal(&Foo::*B, arr, B, 10)) ...
although actually you should probably generalize it:
template<typename T, typename E>
bool all_equal(E T::* member, const T* obj_array, const E* elem_array, size_t n) {
for (int i=0; i<n; ++i) {
if (obj_array[i].*member != elem_array[i]) return false;
}
return true;
}
(Danger: all code above is untested and may consist entirely of bugs.)