3

I'm currently having a real difficult time trying to sort my array (I really need to work on this, I always have trouble sorting data).

So basically I have created a C++ struct that contains a "date" and "snowDepth" element. I have used bubblesort logic to sort the snowDepth element, which is awesome and that's exactly what I need. However, since the elements are separate, the dates don't change. How can I sort a parallel array??

Data should look like this:

  Snow Report  December 12 - 18
      Date            Base
       13             42.3
       12             42.5
       14             42.8
       15             43.1
       18             43.1
       16             43.4
       17             43.8

and mine looks like this:

    SNOW REPORT    December 12 - 18
=======================================
       DATE             DEPTH
        12              42.3
        13              42.5
        14              42.8
        15              43.1
        16              43.1
        17              43.4
        18              43.8

Notice how the dates aren't sorted along with the snowDepth element. How can I accomplish this?

Okay, thanks for all the comments guys!! Here is my code:

// Structure
struct Array
{
    int date;
    double snowDepth;

    void SnowData(int d, double sD)
    {
        date = d;
        snowDepth = sD;
    }
};

// Main Function
int main()
{
    /* *--------------------*
       |      VARIABLES     |
       *--------------------* */

    // Constant Variables
    const int NUM_ELEMENTS = 7;
    const int NUM_MONTHS = 12;

    // String, Numerical, and Boolean Variables
    string month;
    int startDate;
    int endDate;
    int a;
    int b;
    int flag = 0;
    double result;
    bool monthMatch = false;

    // Array Variables
    Array snowData[NUM_ELEMENTS];
    string name[NUM_MONTHS] = {"January", "February", "March",
                               "April",   "May",      "June",
                               "July",    "August",   "September",
                               "October", "November", "December"  };


    /* *--------------------*
       | USER  INSTRUCTIONS |
       *--------------------* */    

    cout << endl;
    cout << "This program will keep track of the local snow conditions for 7 days." << endl;
    cout << "This program will ask the user for the month, date range (must be 7 days)," << endl;
    cout << "and the inches of snow reported on that date.  The program will then sort" << endl;
    cout << "the data from least amount of snow reported to the most." << endl;
    cout << endl;


    /* *--------------------*
       |     USER PROMPT    |
       *--------------------* */

    // Prompt the user for the month
    cout << "Enter the full name of the month: ";
    cin >> month;

    // Prompt the user for the starting date
    cout << "Enter the starting date of the 7-day period: ";
    cin >> startDate;
    snowData[0].date = startDate;

    // Once the user enters the start date, run a loop to initialize the rest of the dates in snowData array
    for (int x = 1; x < NUM_ELEMENTS; x++)
        snowData[x].date = ++startDate;

    // Prompt the user for the ending date
    cout << "Enter the ending date of the 7-day period: ";
    cin >> endDate;
    cout << endl;

    // If the user does not enter the correct ending date (which is 7 days from the start date), loop will prompt the user again
    if (endDate != snowData[NUM_ELEMENTS - 1].date)
    {
        do
        {
            cout << "The end date entered is not 7 days from the start date. Try again." << endl;
            cout << "Enter the ending date of the 7-day period: ";
            cin >> endDate;
            cout << endl;

        } while (endDate != snowData[NUM_ELEMENTS - 1].date);

        int x = 0;

        // Once the user enters the correct ending date, prompt the user for the snow depth numbers
        for (int y = 0; y < NUM_ELEMENTS; y++)
        {
            cout << "Enter the depth of snow measured on " << month << " " << snowData[x].date << ": ";
            cin >> snowData[y].snowDepth;
            x++;
        }
    }

    // Once the user enters the correct ending date, prompt the user for the snow depth numbers
    else
    {
        int x = 0;

        for (int y = 0; y < NUM_ELEMENTS; y++)
        {
            cout << "Enter the depth of snow measured on " << month << " " << snowData[x].date << ": ";
            cin >> snowData[y].snowDepth;
            x++;
        }
    }

    /* *--------------------*
       |  SORTING & FORMAT  |
       *--------------------* */

    // Sorting logic in ascending order
    for (a = 1; (a <= NUM_ELEMENTS) && flag; a++)
    {
        flag = 0;

        for (b = 0; b < (NUM_ELEMENTS - 1); b++)
        {
            if (snowData[b + 1].snowDepth < snowData[b].snowDepth)
            {
                result  = snowData[b].snowDepth;                
                snowData[b].snowDepth = snowData[b + 1].snowDepth;                
                snowData[b + 1].snowDepth = result;

                flag = 1;
            }
        }
    }

    // Formatted Output
    cout << endl;
    cout << "    SNOW REPORT    ";
    cout << month << " " << snowData[0].date << " - " << snowData[6].date << endl;
    cout << "=======================================" << endl;
    cout << setw(11) << "DATE" << setw(18) << "DEPTH" << endl;

    for (int x = 0; x < (NUM_ELEMENTS); x++)
        cout << setw(10) << snowData[x].date << setw(18) << snowData[x].snowDepth << endl;

    cout << endl;
}
mmendoza27
  • 55
  • 1
  • 7
  • 1
    If you post the code for the struct and your sort then it will be easier to find out what's wrong. – ChrisWue Apr 01 '11 at 06:38
  • Agreed. Even just a snippet of it. – jonsca Apr 01 '11 at 06:43
  • Perhaps some of your confusion on this project comes from thinking you have *parallel arrays* at all. You don't. You just have one array. It's an array of structs, so each *single* element of the array has *multiple values* in it. But there's still just one array. If you were *supposed* to have parallel arrays, then you'll need to discard your `Array` data type entirely. I suggest you take this code to your instructor to confirm that you're heading in the right direction instead of wasting too much time. – Rob Kennedy Apr 02 '11 at 18:40

7 Answers7

2

You could create a struct for a single date/snowDepth pair, and keep an array of such pairs. After you could add more fields there, and sort by whatever you like. The data will always be consistent.

By the way: why bubble sort? Quicksort is much faster.

weekens
  • 8,064
  • 6
  • 45
  • 62
  • It sounds like the OP *is* holding both in a struct. This is making me very confused...I might have to make a thread about figuring out this one. ;) – jonsca Apr 01 '11 at 06:39
  • 3
    good advice, but don't run out and use quicksort, use std::sort. It is guaranteed to run in n log n time, and as an STL user, you shouldn't care whether it's implemented as a hybrid form of quicksort or magical elves. It gets the job done without reinventing the wheel. – Jeff Linahan Apr 01 '11 at 07:13
  • 1
    Exactly how much faster is quicksort than bubble sort at sorting a list of seven items? I strongly suspect this is a homework assignment (the C++ class I taught had a "parallel arrays" assignment, too). The students are probably expected to write their own sorting code, in which case bubble sort is much easier to write and much easier to draw on paper. @Jonsca, it's possible that there is *one struct* holding two fields that are both arrays, rather than one array holding structs with two fields each. – Rob Kennedy Apr 01 '11 at 08:31
1

Since both elements is in the same struct, the two elements should move together. Your array should be an array of structs. How do you have it configured now?

jonsca
  • 10,218
  • 26
  • 54
  • 62
1

Update Added a std::map based example for good measure (see below)

I wouldn't work on sorting as much as on datastructures :)

Here is a sample that I wrote in order to brush-off the dust of my C++ skills :) Sorry if I threw in more than the kitchen sink.

Also note, that for this 'tivial' datatype you could probably use std::pair as the 'Report' struct and std::map as the container type, requiring substantially less manual coding, but I wanted to show you how things are done if you coded the struct manually:

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

static struct Report 
{ 
    int date; 
    double base; 
    Report(int d, double b) : date(d), base(b) {} 

    bool operator<(const Report& rhs) const
    {
        return (date<rhs.date) || ((date==rhs.date) && (base<rhs.base));
    }

    friend std::ostream& operator<<(std::ostream& os, const Report& r)
    {
        os << "Report(" << r.date << ", " << r.base << ")";
    }
} theData[] = { 
       Report( 13, 42.3),
       Report( 12, 42.5),
       Report( 14, 42.8),
       Report( 15, 43.1),
       Report( 18, 43.1),
       Report( 16, 43.4),
       Report( 17, 43.8),
   };


int main(int argc, const char* args[])
{
    Report *const begin = theData;
    Report *const end   = theData+(sizeof(theData)/sizeof(*theData));

    // simpler container, take a copy for comparison
    std::vector<Report> simplerData(begin, end);

    // just sort it
    std::sort(begin, end);

    // VERIFY by printing to console
    std::cout << "Verify sorted array: " << std::endl;
    std::copy(begin, end, std::ostream_iterator<Report>(std::cout, "\n"));
    std::cout << "=====================" << std::endl;

    std::cout << "Verify unsorted copy:" << std::endl;
    std::copy(simplerData.begin(), simplerData.end(), std::ostream_iterator<Report>(std::cout, "\n"));
    std::cout << "=====================" << std::endl;

    // Sort that as well - for fun, reversed
    std::sort(simplerData.begin(), simplerData.end(), std::not2(std::less<Report>()));

    std::cout << "Verify reversed copy:" << std::endl;
    std::copy(simplerData.begin(), simplerData.end(), std::ostream_iterator<Report>(std::cout, "\n"));
    std::cout << "=====================" << std::endl;
}

Compile and run using:

g++ -o test test.cpp
./test

Output:

Verify sorted array: 
Report(12, 42.5)
Report(13, 42.3)
Report(14, 42.8)
Report(15, 43.1)
Report(16, 43.4)
Report(17, 43.8)
Report(18, 43.1)
=====================
Verify unsorted copy:
Report(13, 42.3)
Report(12, 42.5)
Report(14, 42.8)
Report(15, 43.1)
Report(18, 43.1)
Report(16, 43.4)
Report(17, 43.8)
=====================
Verify reversed copy:
Report(18, 43.1)
Report(17, 43.8)
Report(16, 43.4)
Report(15, 43.1)
Report(14, 42.8)
Report(13, 42.3)
Report(12, 42.5)
=====================

For completeness, here's what it would look like when using std::map as I suggested above. Note that ordering by keys is implicit:

namespace std // hacky, google Koenig lookup...
{
    template <class K, class V> static std::ostream& operator<<(std::ostream& os, const std::pair<K, V> p)
        { os << "[" << p.first << ", " << p.second << "]"; }
}

void static UsingStdMap()
{
    typedef std::map<int, double> Reports;
    typedef Reports::value_type Report;

    static const Report initializer[] = {Report(13,42.3),Report(12,42.5),Report(14,42.8),Report(15,43.1),Report(18,43.1),Report(16,43.4),Report(17,43.8)};
    Reports theMap(initializer, initializer+sizeof(initializer)/sizeof(*initializer));

    std::copy(theMap.begin(), theMap.end(), std::ostream_iterator<Report>(std::cout, "\n"));
}

Did I mention I'm a bit lazy? Hence the hack to display the items using stream operator. To use custom ordering, see here for example

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I'm afraid, your < operator is not strict. What if we have (20, 43.1) and (19, 44.2)? Is first < second ? Why ? Are we sorting by date or by base ? – weekens Apr 01 '11 at 07:21
  • @weekens: oops fixed (and primarily by base, see question) – sehe Apr 01 '11 at 08:21
  • after adding a std::map based example, I noticed [FrankH's answer](http://stackoverflow.com/questions/5509904/sorting-two-sets-of-data-in-parallel-array/5510822#5510822) which takes fewer shortcuts and is very concise – sehe Apr 01 '11 at 09:30
1

Since you've asked this under a C++ tag, here's one way in C++:

typedef std::pair<Date, double> SnowReportItem;
std::vector<SnowReportItem> SnowReport;
struct sort_criteria {
    bool operator()(const SnowReportItem &a, const SnowReportItem &b)
    { return a.second < b.second; }
};

SnowReport.push_back(make_pair(Date("12 Feb 2011"), 42.5));
// similar ones for the others ...

std::sort(SnowReport.begin(), SnowReport.end(), sort_criteria);

Also, see this stackoverflow question.

EDIT: std::pair.second is not a function.

Community
  • 1
  • 1
FrankH.
  • 17,675
  • 3
  • 44
  • 63
  • This should read `struct sort_criteria_t { ... } sort_criteria;` Otherwise a "does not refer to a value" compiler error is issued. – Jason Feb 04 '15 at 21:05
1

If your homework assignment is specifically about learning to write a sorting algorithm, replace this code:

// Sorting logic in ascending order
...
            result  = snowData[b].snowDepth;                
            snowData[b].snowDepth = snowData[b + 1].snowDepth;                
            snowData[b + 1].snowDepth = result;
...

with this:

// Sorting logic in ascending order
...
            Array tmp  = snowData[b];                
            snowData[b] = snowData[b + 1];                
            snowData[b + 1] = tmp;
...

If your assignment is not about learning to write a sort, use std::sort:

bool Array::operator<(const Array& rhs) const { return this->snowDepth < rhs.snowDepth; }
...
// Sorting logic in ascending order
std::sort(snowData, snowData+NUM_ELEMENTS);

P.s. Your data type Array is misnamed. It doesn't hold an array, it holds a single observation.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
0

You're already storing the data in a struct, so the simplest thing would actually be to just define operator< and swap for your Array type.

struct Array
{
    int date;
    double snowDepth;

    void SnowData(int d, double sD)
    {
        date = d;
        snowDepth = sD;
    }

    friend bool operator<(const SnowData &a, const SnowData &b)
    {
        return a.date < b.date;
    }

    friend void swap(SnowData &a, SnowData &b)
    {
        using std::swap;
        swap(a.date, b.date);
        swap(a.snowDepth, b.snowDepth);
    }
};

// ...later... sort the array
std::sort(snowData, snowData + NUM_ELEMENTS);
monkey0506
  • 2,489
  • 1
  • 21
  • 27
0

You were so close! It's correct that you compare the snowDepth values of the array elements, but you shouldn't just re-arrange those values. Instead, you should re-arrange the entire Array value.

Instead of this:

result  = snowData[b].snowDepth;
snowData[b].snowDepth = snowData[b + 1].snowDepth;
snowData[b + 1].snowDepth = result;

Do this:

Array temp = snowData[b];
snowData[b] = snowData[b + 1];
snoData[b + 1] = temp;

That will move both the snowDepth value and the date value (because they're members of the same struct).

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • -1 for suggesting/not discouraging to write one's own sorting algorithm. Clearly the matter is too advanced for the asker, and there should not ever be a need to implement a custom sort algorithm when stl define nth_element, stable_partition, partial_sort etc etc etc... even inplace_merge!!! For optimizing fests you can use make_heap and family. (_When there should be an exception to this rule, the person will really know_) – sehe Apr 01 '11 at 09:32
  • @sehe The OP appears to be in an entry level class. My assumption from past experience is that professors don't want the students to use the library functions before they understand how to do it for themselves. – jonsca Apr 01 '11 at 11:07
  • @jonsca: That kind of conflicts with asking here, then, doesn't it – sehe Apr 01 '11 at 11:09
  • @sehe I don't see a conflict at all. Students are permitted to ask questions here, they don't have to be seeking what a professional may think of as an optimal answer. – jonsca Apr 01 '11 at 11:10
  • @jonsca: Apparently my psychic ability has lesser reception at the moment than yours. You are probably right – sehe Apr 01 '11 at 11:15
  • @Sehe, I've taught introductory C++, and Mmendoza27's question sounds very similar to assignments from my classes. (I'm not saying it's an assignment from my class, just that I imagine lots of introductory C++ classes have very similar assignments.) Perhaps I should have been more clear about my assumption in my answer. – Rob Kennedy Apr 01 '11 at 14:59
  • Instead of explicitly making a copy, a proper swap method should be implemented to work on the SnowData type. – monkey0506 Mar 01 '16 at 21:42
  • There's no need for that, @Monkey. The `std::swap` function should suffice. But that's not the issue here. The manual swapping technique employed here is just fine, especially for a student just learning about basic programming concepts. – Rob Kennedy Mar 01 '16 at 23:24
  • @RobKennedy I suppose std::swap is sufficient for this simple class, but swap is a trivial function to implement and it's good practice to implement your own anyway (where it makes sense). In any case, understanding *how* to swap values is useful, but I think it's worth saying that (aside from implementing a swap function) this isn't "how it's done". I don't really advocate teaching people the wrong way of doing things just for the sake of simplicity. – monkey0506 Mar 02 '16 at 01:52