Hussein
Hussein

Reputation: 187

Custom Sort on an array of Strings

Given an array of strings containing the seven colors of the rainbow but placed in random order, I am somehow supposed to sort this array to output Red, Orange, Green,....,Violet in that order. The order of rainbow colors. How can I sort this array?

Upvotes: 5

Views: 1731

Answers (3)

luk32
luk32

Reputation: 16070

This code is not completed. But you should get the general idea. One thing I did skip is the sorting of integers itself. Since it should be trivial. As you can see, the mapping, is a little bit of PIA and looks quite bad. But since you forbid to use STL there is no std::map. Moreover, I implied static size of N for all the tables. It could be allocated dynamically, no problem, and no std::vector.

I used else ifs for map* functions to mimick std::map functionality. Probably switch ... case could be used, but it should work pretty much the same on a decent compiler.

The code I wrote below is pretty much the same in terms of functionality provided as Armen's does. I would recommend his solution. I skipped same parts. So you can see it's uglier and more typing. It looks almost like pure C. Maybe with one modification if you really yearn for speed at very large cases. That would be using a temporary data structure that would hold mapped values, to sort it, and then map it back. Precisely I would advise to avoid calling map::operator[](const &T) (or any accessor) on std::string under high performance constraints to avoid hash computations. But that's only it.

There is also some more to discuss. Like what if you wanted two colors to have the same value, or use non-integer weights. STL based solution is way more adaptable.

/* This will map color literals (color names) to integers, which will associate them with 
   a numerical value, than can be used for comparison */
enum Colors { Red, Orange, Green, /*...*/ Violet };

/* this should read colors as std::string instances from the input array and assing
   the the appropriate color codes into output array at corresponding indexes     */
void mapString2Color( const std::string* input, int* output, size_t N ){
  for(size_t i = 0; i < N; i++){
    if ( input[i] == std::string("red") ) output[i] = Colors::Red;
    else if ( input[i] == std::string("orange") ) { output[i] = Colors::Orange; }
    else if ( input[i] == std::string("green") )  { output[i] = Colors::Green;  }
    /*...*/
    else if ( input[i] == std::string("violet") ) { output[i] = Colors::Violet; }
    else {/*unspecified color code */}
  }
}
/* this is supposed to do the opposite to mapString (i.e. put appropriate 
   string at output[i] based on input[i])  */
void mapColor2String( const int* input, std::string* output, size_t N ){
  for(size_t i = 0; i < N; i++){
    if ( input[i] == Colors::Red ) output[i] = std::string("red");
    else if ( input[i] == Colors::Orange ) { output[i] = std::string("orange"); }
    else if ( input[i] == Colors::Green  ) { output[i] = std::string("green");  }
    /*...*/
    else if ( input[i] == Colors::Violet ) { output[i] = std::string("violet"); }
    else {/*unspecified color index*/}
  }
}

void sort(int* array, size_t N){
 /* any in-place sort of your liking for table of (unsigned) integers */
}

main(){
  std::string[N] input_array;
  std::string[N] output_array;
  int[N] temp_array;

  //map (translate) colors to their numerical values
  mapString2Color(input_array, temp_array, N);
  //sort it
  sort(temp_array, N);
  //map (translate) the values back to color names
  mapColor2String(temp_array, output_array, N);
}

Upvotes: 1

Component 10
Component 10

Reputation: 10497

First thing I would do is to create a mapping. You could do this either via a map or by linearly iterating over a presorted array of strings and taking the index of the matching entry. A very simple way (for demonstration purposes really) might be simply to encode the logic into an encapsulated function as follows:

int intForCol( const string& col ) 
{
    if ( col == "red" ) return 0; 
    else if ( col == "orange" ) return 1;
    else if ( col == "yellow" ) return 2;
    else if ( col == "green" ) return 3;
    else if ( col == "blue" ) return 4;
    else if ( col == "indigo" ) return 5;
    else if ( col == "violet" ) return 6;
    throw "Invalid colour"; 
}

This will provide an ordering integer based on the input string. The next step is to create a comparator:

int colComp( const string& lhs, const string& rhs )
{
    return intForCol( lhs ) - intForCol( rhs );
}

This will allow us to compare strings together returning negative if lhs < rhs and positive if lhs > rhs

This can now be used within the STL - either as the comparitor within an associative container or directly in a sort algorithm - with relative ease. Alternatively, if using STL is out of the question or the point of this is to understand how sorting works, you could implement your own sort like the simple and (very) inefficient algorithm below:

    const int col_size = 7;
    string input[col_size];
    input[0] = "indigo";
    input[1] = "green";
    input[2] = "red";
    input[3] = "blue";
    input[4] = "yellow";
    input[5] = "violet"; 
    input[6] = "orange";

    // simple bubble sort
    int passes = col_size;
    int last = col_size; 
    while ( passes-- )
    {
        for ( int i = 0; i < last - 1; ++i ) 
            if ( colComp( input[i], input[i+1] ) > 0 )
            {
                string temp = input[i]; input[i] = input[i+1]; input[i+1] = temp;
            }
        last--;
    }

Upvotes: 0

Armen Tsirunyan
Armen Tsirunyan

Reputation: 133004

You should write a custom comparator. Here's how I would go about it.

//somewhere in initalization code;
std::map<string, int> mapOrder;
mapOrder["red"] = 1;
mapOrder["orange"] = 2;
...
mapOrder["violet"] = 7;


bool isRainbowLess(const std::string& a, const std::string& b)
{
    return mapOrder[a] < mapOrder[b];
}


int main()
{
    std::vector<std::string> myVector;
    ....
    std::sort(myVector.begin(), myVector.end(), &isRainbowLess);
}

Upvotes: 4

Related Questions