C++: Equality of struct of strings - c++

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;
}

Related

Simpler, portable way to iterate over all char values?

For a class I'm teaching I needed to write a piece of code to iterate over all possible char values. I wanted to do this in a way that was portable across different C++ implementations. This meant that
I can't assume char is signed or unsigned,
I can't assume char is eight bits,
I can't assume that sizeof(int) > 1,
I can't assume the int32_t etc. types exist,
I can't assume that integer overflow will not trap,
etc.
The best solution I came up with was the following unusual do ... while loop:
char ch = numeric_limits<char>::min();
process(ch);
do {
ch++;
process(ch);
} while (ch != numeric_limits<char>::max());
This approach works, but it feels pretty clunky to me. It intuitively makes sense that an approach like this might be needed because the number of increments is one more than the number of possible characters, but perhaps there's a fundamentally different approach available that more closely resembles a traditional for loop.
Is there a portable way to achieve this that's less clunky than this?
but perhaps there's a fundamentally different approach available that more closely resembles a traditional for loop.
Well, you can use an actual for loop:
#include <limits>
#include <iostream>
void process(char ch)
{
std::cout << static_cast<long>(ch) << ' ';
}
int main()
{
for ( char ch = std::numeric_limits<char>::min(); ; ++ch )
{
process(ch);
if ( ch == std::numeric_limits<char>::max() )
break;
}
std::cout << '\n';
}
You can create a wrapper class that looks like a container.
struct Chars {
enum {
Chars_MIN = std::numeric_limits<char>::min(),
Chars_MAX = std::numeric_limits<char>::max()
};
struct Iterator {
char c_;
bool end_;
Iterator (char c, bool e = false) : c_(c), end_(e) {}
auto & operator ++ () {
if (c_ == Chars_MAX) end_ = true;
else ++c_;
return *this;
}
auto operator * () const { return c_; }
auto operator == (const Iterator &other) const {
return c_ == other.c_ && end_ == other.end_;
}
auto operator != (const Iterator &other) const {
return !(*this == other);
}
};
Iterator begin () const { return Iterator(Chars_MIN); }
Iterator end () const { return Iterator(Chars_MAX, true); }
};
Then, you can use the modern for syntax:
Chars ccc;
for (auto x : ccc) {
process(x);
}

How to sort vector of class contain CString?

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.

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;
}

Forming the maximum number out of the available numbers?

I am trying to solve the following question https://www.interviewbit.com/problems/largest-number/ : Given a list of non negative integers, arrange them such that they form the largest number.
For example:
Given [3, 30, 34, 5, 9], the largest formed number is 9534330.
Note: The result may be very large, so you need to return a string instead of an integer.
I have been able to solve it and implemented it, using comparison based sorting technique. That is, given two numbers X and Y, I compare two numbers XY (Y appended at the end of X) and YX (X appended at the end of Y). If XY is larger, then X should come before Y in output, else Y should come before. The following is the code:
string Solution::largestNumber(const vector<int> &A) {
// Do not write main() function.
// Do not read input, instead use the arguments to the function.
// Do not print the output, instead return values as specified
// Still have a doubt. Checkout www.interviewbit.com/pages/sample_codes/ for more details
vector<string> myvec;
for (int i = 0; i < A.size(); i++)
{
string s = to_string(A[i]);
myvec.push_back(s);
}
sort(myvec.begin(),myvec.end(),mycomp());
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
}
struct mycomp
{
inline bool operator() (const string &p1, const string &p2)
{
string s1 = p1.append(p2);
string s2 = p2.append(p1);
if (s1.compare(s2) < 0)
return false;
else
return true;
}
};
But, the problem is, I have to merge the two functions into a single one because I just have to implement the single function. I cannot define one more function since I have no control over the entire piece of code (look at the link's submission part). Therefore, my ask is, how can I use the comparator by defining it inside the function string Solution::largestNumber(const vector<int> &A). Thanks!
This is a perfect place for a lambda.
sort(myvec.begin(), myvec.end(), [](const string &p1, const string &p2) {
string s1(p1 + p2);
string s2(p2 + p1);
return s1.compare(s2) >= 0;
});
I changed your code to not call append() on the strings, since you accept them as references to const objects, and p1.append(p2) tries to modify p1, but that's not allowed on a const object. Further, avoid constructs like if(x) return true else return false; and instead just return x;
Also, this
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
Can be condensed to:
string s;
for (auto const& e : myvec)
s += e;
return s;
(Assuming you have a c++11 compiler or later)