3

I have strings like 7X1234 XY1236 NM1235. I want to sort this strings using last 4 numerical digits only ignoring the initial two alphabets. Also, I want to compare those numerical digits to see if they are sequential.

One way to achieve this I can think of is to split these strings between alphabets and numerals as (7X and 1234) and work lexical cast the numeral string to int and work on it. But, how can I associate the alphabet part again to the numeral part that is how to prefix 7X again to 1234 at the end when the numeral strings are sorted and compared in C++?

In short if I have 7X1234 XY1236 NM1235 BV1238 I need to get 7X1234 NM1235 XY1236 BV1238

I did not elaborate that I wanted to find out if the numerical part of strings are sequential. Right now when I have just ints like 1234 1236 1235 1238 I do something like below

            std::vector<int> sortedDigits{1234 1235 1236 1238};
            int count = 1;
            int pos = 0;
            std::vector<std::pair<int, int> > myVec;
            myVec.push_back(std::make_pair(sortedDigits[pos], count));
            for(size_t i = 1; i < sortedDigits.size(); ++i)
            {
                if(sortedDigits[i] != (sortedDigits[i-1] + 1))
                {
                   count = 1;
                   myVec.push_back(std::make_pair(sortedDigits[i], count) );
                   ++pos;
                }
                else
                {
                    sortedDigits[pos].second = ++count;
                }
            }  

So at the end I get (1234, 3) and (1238, 1)

I don't know how can I get something like this when strings are there?

1
  • get the numerical part and call atoi() on it to get the integer it is representing. Commented Aug 9, 2013 at 10:07

7 Answers 7

11

Since the character encoded values of numerals are ordered in the same order as the numbers they represent, you can do string comparison on the last four digits:

#include <cstring>
#include <string>

// Requires: a.size() >= 2, b.size() >= 2
bool two_less(std::string const & a, std::string const & b)
{
    return std::strcmp(a.data() + 2, b.data() + 2) < 0;
}

Now use sort with predicate:

#include <algorithm>
#include <vector>

std::vector<std::string> data { "7X1234", "YX1236" };

std::sort(data.begin(), data.end(), two_less);

In C++11, and in particular if you have no repeated use for this, you can also use a lambda directly in the sort call:

std::sort(data.begin(), data.end(),
         [](std::string const & a, std::string const & b)
         { return std::strcmp(a.data() + 2, b.data() + 2) < 0; });

Then you can even make the number "2" a captured variable if you need to vary it.

Sign up to request clarification or add additional context in comments.

2 Comments

Excellent. Thanks. It does solve the first problem but I can't find sequential numbers as described above.
@polapts: Use std::stoi(a.substr(2)) etc.
2

Use qsort and provide a comparator function that indexes into the start of the string plus an offset of two, rather than directly from the beginning of the string.

For example your comparator function could look like this:

int compare (const void * a, const void * b)
{
    char * a_cmp = ((char *)a)+2;
    char * b_cmp = ((char *)b)+2;
    return strcmp(a_cmp, b_cmp);
}

Comments

1

You can e.g make struct like this

struct combined{
    string alph;
    int numeral;
};

put these in a c++ standard container

and use the sort of algoritm with a user defined compare object.

Comments

1

You should create a class that encapsulates your string and which has an int and and string field. This class can overload the comparison operators.

class NumberedString
{
private:
   int number;
   string originalString;

public:
   NumberedString(string original) { ... }

   friend bool operator> (NumberedString &left, NumberedString &right);
   friend bool operator<=(NumberedString &left, NumberedString &right);

   friend bool operator< (NumberedString &left, NumberedString &right);
   friend bool operator>=(NumberedString &left, NumberedString &right);
};

Comments

1

You can just define your comparator

bool mycomparator(const std::string& a, const std::string& b) {
    return a.substr(2) < b.substr(2);
}

then you can sort your std::vector<std::string> passing mycomparator as third parameter.

In C++11 this is also a case in which an anonymous lambda is a good fit...

#include <vector>
#include <algorithm>
#include <string>
#include <iostream>

int main(int argc, const char *argv[])
{
    std::vector<std::string> data = {"7X1234", "XY1236", "NM1235", "BV1238"};
    std::sort(data.begin(), data.end(),
              [](const std::string& a, const std::string& b) {
                  return a.substr(2) < b.substr(2);
              });
    for (auto x : data) {
        std::cout << x << std::endl;
    }
    return 0;
}

If you're 100% sure that the strings in the array are in XX9999 format you can use instead

return strncmp(a.data()+2, b.data()+2, 4) < 0;

that is more efficient because doesn't require any memory allocation to do the comparison.

2 Comments

Note, however, that all those calls to substr introduce substantial overhead: each one creates and destroys a string object.
I know. I liked it better than using strncmp(a.data()+2, b.data()+2, 4) because of UB in case there are strings not in the expected format. I'll add a note about it.
0

Use a std::map<int, std::string>, using the int value as key and the respective string as value. You can then simply iterate over the map and retrieve the strings; they will already be in sorted order.

Comments

0

How about something like this:

std::string str[] = { "7X1234", "XY1236", "NM1235" };

std::map<int, std::string> m;

for(s : str)
{
    std::ostringstream ss(s.substr(2));
    int num;
    ss >> num;
    m[num] = s;
}
for(i : m)
{
   std::cout << i->second << " ";
}
std::cout << std::endl;

I just typed this in, so minor typos/bugs may be there, but principle should work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.