Home > Enterprise >  Sort String as double
Sort String as double

Time:03-15

I am trying to sort my file contents as double in the ascending order

My input file contains for example these lines:

105 350 4.41386e-06 4.41386e-06 
115 300 4.58965e-06 4.58965e-06 
15  150 1.6457e-06  1.6457e-06  
255 550 5.33661e-05 5.33661e-05 
25  150 3.21907e-06 3.21907e-06 
35  550 2.57952e-05 2.57952e-05 
45  150 1.78332e-06 1.78332e-06 

And i want my output file to have them as following:

15  150 1.6457e-06  1.6457e-06  
25  150 3.21907e-06 3.21907e-06 
35  550 2.57952e-05 2.57952e-05 
45  150 1.78332e-06 1.78332e-06 
105 350 4.41386e-06 4.41386e-06 
115 300 4.58965e-06 4.58965e-06 
255 550 5.33661e-05 5.33661e-05

Since i am just a beginner in C coding, if have tried these line to do that task:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

int main()
{
    std::ifstream textfile( "Limit.txt" );
    std::string text_input;
    std::vector< std::string > sort_vec;
    std::ofstream outfile1;
    TString outfile_name1 = "Limits.txt";
    if ( textfile.is_open() )
    {
        outfile1.open( outfile_name1 );

        while ( std::getline( textfile,text_input ) )
        {  
            sort_vec.push_back(text_input);
        }

        textfile.close();
    }

    std::sort( std::begin( sort_vec ), std::end( sort_vec ), std::less<string>() );
    
    for ( const auto & e : sort_vec )
    {
        outfile1 << e << "\n";
    }

    outfile1.close();
}

But I could not get the results that I want.

Could please help me through.

CodePudding user response:

Because of spaces and different length of data in each line, the lexicographic sorting of lines as strings may produce unexpected results or be totally wrong.

Therefore I would recommend to split the lines into it 4 parts, which is very simple using normal IO-extract-functionality using the >> operator.

The data of one row can be stored in a struct. That is somehow intuitive, readable and understandable. For storing the whole list, we can use a std::vector of such a struct.

Instead of using a struct, we can also use a std::tuple as proposed by Jarod42. A std::tuple has comparison operators, which allow ultra simple sorting of one row later.

Anyway. Let us start with a struct, which has additional possibilities, as shown later.

  1. Define the struct
  2. Open the file and check, if it could be opened
  3. Define a std::vector of the above struct
  4. Create a temporary instance of a struct, te be able to read data into it
  5. In a loop, extract data from the file and store in struct-elements
  6. Push back each just read data into the vector (we will call it database)
  7. Sort with a lambda expression to have full control on how things will be sorted
  8. Show result to the user

This can be coded in many ways. Look at on potential example below:

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iomanip>

struct Data {
    int i1{};
    int i2{};
    double d1{};
    double d2{};
};

int main() {

    // Open the source textfile and check, if it could be opened
    std::ifstream textfile("Limit.txt");
    if (textfile) {

        // Here we will store all data
        std::vector<Data> database{};

        // Temporary storage for 1 line
        Data data{};

        // Read all lines and add data to the database
        while (textfile >> data.i1 >> data.i2 >> data.d1 >> data.d2)
            database.push_back(data);

        // Sort database and use a lambda to do it as wished
        std::sort(database.begin(), database.end(), [](const Data& d1, const Data& d2) {
            return d1.i1 == d2.i1 ? (d1.i2 == d2.i2 ? (d1.d1 == d2.d1 ? d1.d2 < d2.d1 : d1.d1 < d2.d1) : d1.i2 < d2.i2) : d1.i1 < d2.i1; });

        // Debug output
        for (const Data& d : database)
            std::cout << std::left << std::setw(4) << d.i1 << std::setw(4) << d.i2 << std::setw(12) << d.d1 << d.d2 << '\n';
    }
    else std::cerr << "\n*** Error! Could not open source file\n\n";
}

But that is not all.

As you know, C is an object oriented language. And we can store data together with functions, operating on this data, in a struct (a struct is a class).

So, we can add input and output functions and the less than operator that is needed for sorting.

Then the main function will be significantly simpler.

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iomanip>

struct Data {
    int i1{};
    int i2{};
    double d1{};
    double d2{};

    // Methods / operators
    // Input
    friend std::istream& operator >> (std::istream& is, Data& data) {
        return is >> data.i1 >> data.i2 >> data.d1 >> data.d2;
    }
    // Output
    friend std::ostream& operator << (std::ostream& os, const Data& d) {
        return os << std::left << std::setw(4) << d.i1 << std::setw(4)
            << d.i2 << std::setw(12) << d.d1 << d.d2;
    }
    // Comparison
    bool operator < (const Data& other) {
        return i1 == other.i1 ? (i2 == other.i2 ? (d1 == other.d1 ? d2 < other.d1 : d1 < other.d1) : i2 < other.i2) : i1 < other.i1;
    }

};

int main() {

    // Open the source textfile and check, if it could be opened
    std::ifstream textfile("Limit.txt");
    if (textfile) {

        // Here we will store all data
        std::vector<Data> database{};

        // Temporary storage for 1 line
        Data data{};

        // Read all lines and add data to the database
        while (textfile >> data)
            database.push_back(data);

        // Sort database
        std::sort(database.begin(), database.end());

        // Debug output
        for (const Data& d : database)
            std::cout << d << '\n';
    }
    else std::cerr << "\n*** Error! Could not open source file\n\n";
}

Advantage: Understandable and simple. Good control for sorting. Disadvantage: Complex sorting criteria

CodePudding user response:

The issue here, as pointed out in comments, is that std::less<std::string> sorts your strings lexicographically (i.e. the same order as it would appear in a dictionary).

This is not the intended behaviour since the sort will be then performed character by character and not value by value.

To achieve the correct behaviour, you need to make a custom comparator that will extract the double value to perform the sorting based on it.

For example (using a lambda):

std::sort(sort_vec.begin(), sort_vec.end(), [](const std::string & lhs, const std::string & rhs){
    double ld, rd;
    std::stringstream{lhs} >> ld;
    std::stringstream{rhs} >> rd;

    return ld < rd;
});

And it would do the trick (live example here)

CodePudding user response:

Since you want a hierarchical sort based on the columns from left-to-right, there are multiple ways to do this using std::sort.

One way is to read in the values into a std::tuple and apply std::sort on a vector of these tuples. The tuple class will automatically do a lexicographical compare, from left-to-right, of the tuple's components.

The std::tuple<int, int, double, double> basically mimics one line of your data.

#include <iostream>
#include <string>
#include <vector>
#include <tuple>
#include <sstream>

std::string test = 
"105 350 4.41386e-06 4.41386e-06\n"
"115 300 4.58965e-06 4.58965e-06\n" 
"15  150 1.6457e-06  1.6457e-06\n"  
"255 550 5.33661e-05 5.33661e-05\n" 
"25  150 3.21907e-06 3.21907e-06\n" 
"35  550 2.57952e-05 2.57952e-05\n" 
"45  150 1.78332e-06 1.78332e-06\n"
"105 35  4.41386e-06 4.41386e-06\n" 
"115 300 4.58965e-06 4.58965e-09\n" 
"15  150 1.6457e-04  1.6457e-04\n"  
"255 550 5.33661e-05 5.33661e-02";


using DataLine = std::tuple<int, int, double, double>;
using VectorDataLine = std::vector<DataLine>;

int main()
{
    std::istringstream textfile(test);
    VectorDataLine sort_vec;
    DataLine data_line;
    while ( textfile >> std::get<0>(data_line) >> 
                        std::get<1>(data_line) >> 
                        std::get<2>(data_line) >> 
                        std::get<3>(data_line)) 
    {  
       sort_vec.push_back(data_line);
    }
    std::sort( std::begin( sort_vec ), std::end( sort_vec ));

    for ( const auto & e : sort_vec )
    {
        std::cout << std::get<0>(e) << ' ' <<
                     std::get<1>(e) << ' ' << 
                     std::get<2>(e) << ' ' << 
                     std::get<3>(e)  << "\n";
    }
}

Output:

15 150 1.6457e-06 1.6457e-06
15 150 0.00016457 0.00016457
25 150 3.21907e-06 3.21907e-06
35 550 2.57952e-05 2.57952e-05
45 150 1.78332e-06 1.78332e-06
105 35 4.41386e-06 4.41386e-06
105 350 4.41386e-06 4.41386e-06
115 300 4.58965e-06 4.58965e-09
115 300 4.58965e-06 4.58965e-06
255 550 5.33661e-05 5.33661e-05
255 550 5.33661e-05 0.0533661
  • Related