Efficient sorting algorithm for 3 vectors - c++

I have 3 vectors, category description and price I have written this piece of code to put the vectors organised by category into a file called menuFile:
for(int x = 0; x < _category.size(); x++){
if(_category[x].compare("Starter") == 0){
menuFile << _category[x] << ":" << _description[x] << ":" << _price[x] << endl;
}
}
for(int x = 0; x < _category.size(); x++){
if(_category[x].compare("Main") == 0){
menuFile << _category[x] << ":" << _description[x] << ":" << _price[x] << endl;
}
}
for(int x = 0; x < _category.size(); x++){
if(_category[x].compare("Pudding") == 0){
menuFile << _category[x] << ":" << _description[x] << ":" << _price[x] << endl;
}
}
for(int x = 0; x < _category.size(); x++){
if(_category[x].compare("Drink") == 0){
menuFile << _category[x] << ":" << _description[x] << ":" << _price[x] << endl;
}
}
But this does not seem like a very efficient method. Is the a better way to do this?

I believe you should create a struct to handle the three types of data and then make a vector for it.
for example:
struct Menu {
string category;
string description;
int price;
};
then i would recommend you to implement a comparator to decide on how to sort the data. Let's say sort by the price (You can decide on how to implement the overloaded operator of course).
struct Menu {
string category;
string description;
int price;
bool operator < (const Menu& rhs) const {
return (this->price < rhs.price);
}
};
then just make a vector for this struct and sort it.
vector<Menu> menu;
// do something, insert data
sort(menu.begin(),menu.end());
Then output accordingly.
for(int x = 0; x < menu.size(); x++){
menuFile << menu[x].category << ":" << menu[x].description << ":" << menu[x].price << endl;
}

I don't know what your container types are, so I'm going to assume std::string. The simplest thing might be to just make a separate vector of tuples:
using StrTuple = std::tuple<std::string*, std::string*, std::string*>;
std::vector<StrTuple> combined;
for (size_t i = 0; i < _category.size(); ++i) {
combined.emplace_back(&_category[i], &_description[i], &_price[i]);
}
And then sort that one by category:
std::sort(std::begin(combined), std::end(combined),
[](const StrTuple& lhs, const StruTuple& rhs) {
return *std::get<0>(lhs) < *std::get<0>(rhs);
});
And then just stream it in order:
for (auto& t : combined) {
menuFile << *std::get<0>(t) << ":" << *std::get<1>(t) << ":" << *std::get<2>(t) << std::endl;
}
The same could be achieved with a separate type instead, something like:
struct CombinedValues {
std::string *category, *description, *price;
};

I do not think there is a much more efficient algorithm to do that. You may think so because you do 4 loop with the same thing. But you are still doing O(n) efficiency. Adding a sort operation like some proposition above would add a step running at O(n*log(n)) efficiency, which is way worse.
You still need the 4 loops, so the only thing we can try to optimize is the testing, that is replacing the string compare() operation in each loop by something faster. Something possible would be to replace the string test by an integer test, meaning pre-computing a category number in a new vector (i.e. a value 0 if category=="Starter", 1 if "Main", etc and a value different from 0..3 if not any of the interesting category (if that is possible)
That would mean an initial loop to compute this category, which can be made more efficient (using an average of 2 string comparison). Or even less using a hash map of the category string.
So in the loops, we just do an integer comparison instead of a string comparison for each element. However, we add the time to compute the category number in the first loop. It is not obvious at first glance which will be faster: 4 loops and 4*n string comparisons or 4 loops, 2*n string comparisons and 4*n integer comparisons. That may be faster, if comparing strings are much more costly than comparing integers.
For this kind of stuff, only way to know is to measure actual execution time. Obviously, all this is time consuming, so only to be done if this is really necessary (i.e. your profiler tells you that this part needs to be optimized)

Related

Mean and Mode of vector array - How can I make a smaller improvement in the function

Doing an exercise to find the mean and mode of a list of numbers input by a user. I have written the program and it works, but I'm wondering if my function 'calcMode' is too large for this program. I've just started looking into functions which is a first attempt. Would it be better to write smaller functions? and if so what parts can I split? Im pretty new to C++ and also looking if I can improve this code. Is there any changes I can make to make this run more efficient?
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int calcMean(vector<int> numberList)
{
int originNumber = numberList[0];
int nextNumber;
int count = 0;
int highestCount = 0;
int mean = 0;
for (unsigned int i = 0; i <= numberList.size() - 1; i++)
{
nextNumber = numberList[i];
if (nextNumber == originNumber)
count++;
else
{
cout << "The Number " << originNumber << " appears " << count << " times." << endl;
count = 1;
originNumber = nextNumber;
}
}
if (count > highestCount)
{
highestCount = count;
mean = originNumber;
}
cout << "The Number " << originNumber << " appears " << count << " times." << endl;
return mean;
}
int main()
{
vector<int> v;
int userNumber;
cout << "Please type a list of numbers so we can arrange them and find the mean: "<<endl;
while (cin >> userNumber) v.push_back(userNumber);
sort(v.begin(), v.end());
for (int x : v) cout << x << " | ";
cout << endl;
cout<<calcMean(v)<<" is the mean"<<endl;
return 0;
}
One thing to watch out for is copying vectors when you don't need to.
The function signature
int calcMode(vector<int> numberList)
means the numberList will get copied.
int calcMode(const & vector<int> numberList)
will avoid the copy. Scott Meyer's Effective C++ talks about this.
As an aside, calling is a numberList is misleading - it isn't a list.
There are a couple of points that are worth being aware of in the for loop:
for (unsigned int i = 0; i <= numberList.size()-1; i++)
First, this might calculate the size() every time. An optimiser might get rid of this for you, but some people will write
for (unsigned int i = 0, size=numberList.size(); i <= size-1; i++)
The size is found once this way, instead of potentially each time.
They might even change the i++ to ++i. There used to a potential overhead here, since the post-increment might involve an extra temporary value
One question - are you *sure this gives the right answer?
The comparison nextNumber == originNumber is looking at the first number to begin with.
Try it with 1, 2, 2.
One final point. If this is general purpose, what happens if the list is empty?
Would it be better to write smaller functions?
Yes, you can make do the same job using std::map<>; which could be
a much appropriate way to count the repetition of the array elements.
Secondly, it would be much safer to know, what is the size of the
array. Therefore I suggest the following:
std::cout << "Enter the size of the array: " << std::endl;
std::cin >> arraySize;
In the calcMode(), you can easily const reference, so that array
will not be copied to the function.
Here is the updated code with above mentioned manner which you can refer:
#include <iostream>
#include <algorithm>
#include <map>
int calcMode(const std::map<int,int>& Map)
{
int currentRepetition = 0;
int mode = 0;
for(const auto& number: Map)
{
std::cout << "The Number " << number.first << " appears " << number.second << " times." << std::endl;
if(currentRepetition < number.second )
{
mode = number.first; // the number
currentRepetition = number.second; // the repetition of the that number
}
}
return mode;
}
int main()
{
int arraySize;
int userNumber;
std::map<int,int> Map;
std::cout << "Enter the size of the array: " << std::endl;
std::cin >> arraySize;
std::cout << "Please type a list of numbers so we can arrange them and find the mean: " << std::endl;
while (arraySize--)
{
std::cin >> userNumber;
Map[userNumber]++;
}
std::cout << calcMode(Map)<<" is the mode" << std::endl;
return 0;
}
Update: After posting this answer, I have found that you have edited your function with mean instead of mode. I really didn't get it.
Regarding mean & mode: I recommend you to read more. Because in general, a data set can have multiple modes and only one mean.
I personally wouldn't split this code up in smaller blocks, only if i'd want to reuse some code in other methods. But just for this method it's more readable like this.
The order of excecution is aroun O(n) for calc which is quite oke if you ask me

C++ Data Structure: Can't display string corresponding to the largest number

My first C++ class coming from a basic Java class. This class is a more advanced C++ programming class about Data Structures. I don't know the basics of C++, only a little basics of Java.
Assignment is to :
-get 3 user inputs of states and their population (done).
-Get the most populated (biggest of three) and post it. (1/2)
I am able to get the highest number... but I'm not sure on the syntax on how to post it with the corresponding string (state).
I know this is some kind of array using struct, but I dont know how to post st.title
#include "stdafx.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
struct states_t {
string statename;
int population;
} state[3];
int main()
{
string mystr;
int n;
for (n = 0; n<3; n++)
{
cout << "Enter state name: ";
getline(cin, state[n].statename);
cout << "Enter population: ";
getline(cin, mystr);
stringstream(mystr) >> state[n].population;
}
cout << "\nYou have entered these movies:\n";
for (n = 0; n < 3; n++)
cout << state[n].statename << "\n" << state[n].population << "\n";
return 0;
}
==== UPDATED CODE WITH LARGEST POPULATION ====
#include "stdafx.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
struct states_t {
string statename;
int population;
} state[3];
int main()
{
string mystr;
int n;
for (n = 0; n<3; n++)
{
cout << "Enter state name: ";
getline(cin, state[n].statename);
cout << "Enter population: ";
getline(cin, mystr);
stringstream(mystr) >> state[n].population;
}
cout << "\nYou have entered these states:\n";
for (n = 0; n < 3; n++)
cout << state[n].statename << " " << state[n].population << "\n" << "\n";
if ((state[0].population >= state[1].population) && (state[0].population >= state[2].population))
cout << "The most populous state you entered is: " << state[0].statename << " with a population of " << state[0].population << "\n";
else if ((state[1].population >= state[0].population) && (state[1].population >= state[2].population))
cout << "The most populous state you entered is: " << state[1].statename << " with a population of " << state[1].population << "\n";
else
cout << "The most populous state you entered is: " << state[2].statename << " with a population of " << state[2].population << "\n";
return 0;
}
The first step is to store the name of each state with its population. It will help if you change title to name to make it more clear what the variable is for. If you do this correctly, you will quickly see that you no longer need mystr. (Note that you should always use meaningful variable names. A generic name like mystr often means that you do not know the purpose of the variable. Keep thinking about what the variable is for in order to make a more useful name.)
Now once you have the state data input into the array correclty, you should keep track of the data for the least and most populous state, rather than just its population. Instead of
int mn, mx;
declare
state_t mn, mx;
Then in your if statement do
mn = st[n];
and similarly for mx.
You will have to change your if condition to access the value in the struct. Then you can print the values directly from mn and mx.
Your code is designed to find the highest (and lowest) population of all states. You could also have tried to find out what the index-number is of the state with the highest population and use that number to index the state array to get what you need from there.
Here's how I would do it:
I would first make two int arrays, one corresponding to the index values of the array of struct states_t, and then one corresponding to the population values, like such:
int index[3];
int pop[3];
for (int i = 0; i < 3; i++)
{
index[i] = i;
pop[i] = st[i].population;
}
Next, perform a bubble sort algorithm on the population, and move the indices of the objects around according to the actions of the sort algorithm like such:
int n = 3;
for (int i = 0 ; i < ( n - 1 ); i++)
{
for (int j = 0 ; j < n - i - 1; j++)
{
//if the population of the next element
//is higher than the current element, swap it
//perform the same operation for state indices
if (array[j] > array[j+1])
{
int swap = pop[j];
int swap2 = index[j];
pop[j] = pop[j+1];
index[j] = index[j+1];
pop[j+1] = swap;
index[j+1] = swap2;
}
}
}
All that's left to do now is to call the first object in the list with the index array like such:
st[index[0]].title; //state with highest population
Coolest part about this method is that you can make this work for any number of States by changing the value of int n.
While there is nothing that prevents you from using a standard array-of-struct as you would in C, embracing the C++ std::vector can take a bulk of the tedium out of it. While using the array-of-struct, you get the benefit of protecting your array bounds manually indexing and manually handling storage, C++ has long sought to help alleviate or ease the manual aspects of handling collections of "things" (for lack of better words)
The std::vector container is tailor made to allow you to add to a collection of things using the simple push_back modifier. Basically you define your struct (say s_state_t) holding your name and pop, much as you have, and then declare and create an instance of a vector of type <s_state_t> (say state). Then to add a state (say s) to the vector you simply state.push_back(s) and let std::vector handle the storage and indexing. (vector also provides many other helpful member functions and modifiers to help get information about your collection)
Then the C++ way to approach managing a collection of states is to create either an additional struct or class to manipulate your collections of states, to add to, check and keep track of the max/min populations, etc. In a very simple form, you could create a class, that provides member functions that do just that, add a new state, check the max/min and then provide a way to output the contents of your collection. For example, you could do something like:
#include <vector>
#include <string>
#include <iomanip>
#include <limits>
typedef struct { /* struct holding state name and population */
std::string name;
int pop;
} s_state_t;
class country { /* class country - a collection of states */
std::vector<s_state_t> state; /* declare vector for states */
s_state_t mx = { "", 0 }, /* declare structs for max min */
mn = { "", std::numeric_limits<int>::max() };
void chkmxmn (s_state_t s) { /* function to set max/min */
if (s.pop < mn.pop)
mn = s;
if (s.pop > mx.pop)
mx = s;
}
public:
void addstate (std::string name, int pop) { /* add a state */
s_state_t s = { name, pop }; /* struct for new state */
chkmxmn (s); /* update max and min */
state.push_back (s); /* push_back to vector */
}
void prnstates() { /* output saved states, max/min */
for (auto& i : state) /* loop over vector */
std::cout << std::setw(16) << std::left << i.name <<
std::setw(10) << std::right << i.pop << "\n";
std::cout << "\nminimum and maximum populations:\n" <<
std::setw(16) << std::left << mn.name <<
std::setw(10) << std::right << mn.pop << "\n" <<
std::setw(16) << std::left << mx.name <<
std::setw(10) << std::right << mx.pop << "\n";
}
};
int main (void) {
country us; /* instance of country */
us.addstate ("Texas", 25000000); /* add names/pops */
us.addstate ("Louisiana", 12000000);
us.addstate ("California", 50000000);
us.prnstates(); /* output results */
return 0;
}
(note: you should add additional validations to check the name is not NULL or empty and pop is a reasonable number -- that is left to you)
Example Use/Output
$ ./bin/vector_states
Texas 25000000
Louisiana 12000000
California 50000000
minimum and maximum populations:
Louisiana 12000000
California 50000000
note: you can also create a typedef to your new vector type to cut down on the typing associated with specifying instances and parameters of the type with something similar to:
typedef std::vector<s_state_t> state_t; /* typedef to cut down typing */
which then allows you to declare new instances or parameters as simply, e.g.:
state_t state; /* declare vector for states */
Look things over. Neither method is more "right or wrong", but if you are going to learn C++ instead of C, you might as well go ahead and use the nice parts of it.

Sending a vector of stuct objects through the 'next_permutation' algorithm fails to build

I am having trouble with getting a brute-force algorithm to work. As you can see from my code below, I am trying to evaluate a vector of structs and find the most efficient way to order a series of timed events. Here is my simple struct layout whose objects I am placing in the 'part1Vec' vector:
struct person
{
float swim;
float bike;
float run;
float bikeRun;
person();
person(float swim, float bike, float run)
{
this->swim = swim;
this->bike = bike;
this->run = run;
this->bikeRun = bike + run;
};
};
However, when I compile I get an error within the algorithm class that I have supposedly traced to this line:
while (next_permutation(part1Vec.begin(), part1Vec.end()))
the error message I get is this:
//Invalid operands to binary expression ('const person' and 'const person')
I believe I have the next_permutation function implemented correctly, but I do not understand why it is giving me this error. Any help would be appreciated, thanks. Here is my full code:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct person
{
float swim;
float bike;
float run;
float bikeRun;
person();
person(float swim, float bike, float run)
{
this->swim = swim;
this->bike = bike;
this->run = run;
this->bikeRun = bike + run;
};
};
//function declarations and global variables
vector<person> part1Vec;
vector<person> highestVec;
float highestRating;
float tempRating;
float rating(vector<person> vector);
int main()
{
//PART 1
//make objects
person one(20, 25, 20), two(35, 20, 15), three(40, 20, 30);
//insert into vector
part1Vec.push_back(one);
part1Vec.push_back(two);
part1Vec.push_back(three);
cout << "_________swim__bike__run__" << endl;
for (int i=0; i<part1Vec.size(); i++)
{
cout << "Vector #" << i+1 << ": "
<< part1Vec[i].swim << " "
<< part1Vec[i].bike << " "
<< part1Vec[i].run;
}
cout << endl << "Calculating..." << endl;
//Next permutation function
while (next_permutation(part1Vec.begin(), part1Vec.end())) //Invalid operands to binary expression ('const person' and 'const person')
{
//initialize highestVec
if (highestVec.size() == 0)
{
highestRating = rating(part1Vec);
highestVec = part1Vec;
}
//if Highest Rating is less than current permutation, update.
else if (highestRating < (tempRating = rating(part1Vec)) )
{
highestVec = part1Vec;
highestRating = tempRating;
}
}
cout << "Best Solution:" << endl;
for (int i=0; i<part1Vec.size(); i++)
{
cout << "Vector #" << i+1 << ": "
<< highestVec[i].swim << " "
<< highestVec[i].bike << " "
<< highestVec[i].run;
}
cout << endl << "Rating: " << highestRating << endl;
return 0;
}
float rating(vector<person> thisVector)
{
float rating = 0;
float swimSum = 0;
for (int i=0; i<thisVector.size()-1; i++)
{
swimSum += thisVector[i].swim;
if (rating < swimSum + thisVector[i].bikeRun)
rating = swimSum + thisVector[i].bikeRun;
}
return rating;
}
Kyle Y., did you understand what chris meant?
I hope Chris won't mind, but I'll take the liberty of elaborating just in case.
First up, say what compiler you're using. One reason is that they give different error messages. And in this instance, Invalid operands to binary expression ('const person' and 'const person') was kinda useful but not as useful as it could be. If I ran this through gcc, for example, it'd probably tell me something more like that std::next_permuation was looking for an undefined operator.
std::next_permuation uses a well ordering to generate permutations. So it needs arguments of types with an order defined on themselves, and so that the algorithm will always terminate, a consistent order (an ordering is inconsistent if a<(b) and b<(a) is possible, which is generally inadvisable regardless).
That's what chris is referring to, and the way you do that in C++ with say struct types like yours for which order is not already defined by a base class, is to override bool operator<(... in the struct.
Because you only need the ordering for your permutation generation, any old ordering will do as long as: everything is ordered and the ordering is consistent, as above.
See here for your code again with that override and a few unsigned ints in where they should be, note:
bool operator<(const person& rhs) const {
return (this->swim + this->bike + this->run) < (rhs.swim + rhs.bike + rhs.run);
}
Best.

Why isn't my std::set sorted?

I have a class to store data that looks like this:
class DataLine
{
public:
std::string name;
boost::posix_time::time_duration time;
double x, y, z;
DataLine(std::string _name, boost::posix_time::time_duration _time, double _x,
double _y, double _z); //assign all these, not going to do it here
bool operator < (DataLine* dataLine) { return time < dataLine->time; }
}
Then I read in a bunch of data and .insert it into a std::set of the objects:
std::set<DataLine*> data;
data.insert( new DataLine(newname, newtime, newx, newy, newz) );
//...insert all data - IS OUT OF ORDER HERE
Then I run through my data and do stuff with it while appending new elements to the set.
boost::posix_time::time_duration machineTime(0,0,0);
for(std::set<DataLine*>::reverse_iterator it = data.rbegin(); it != data.rend(); ++it)
{
if(machineTime < (*it)->time)
{
machineTime = (*it)->time;
}
machineTime += processDataLine(*it); //do stuff with data, might add to append list below
for(std::vector<AppendList*>::iterator iter = appendList.begin(); iter != appendList.end(); ++iter)
{
data.insert( new DataLine( (*iter)->name, machineTime,
(*iter)->x, (*iter)->y, (*iter)->z); );
}
}
When I try to loop through the set of data both before and after inserting the elements all my data is out of order! Here are some times outputted when looped using
for(std::set<DataLine*>::iterator it = data.begin(); it != data.end(); ++it)
{
std::cout << std::endl << (*it)->time;
}
14:39:55.003001
14:39:55.003002
14:39:55.001000
14:39:59.122000
14:39:58.697000
14:39:57.576000
14:39:56.980000
Why aren't these times sorted in order?
It is sorted. It's sorted based on the data type you're storing in the set, which is a pointer to a DataLine. In other words, it'll sort according to the location in memory of your objects which is probably creation order (but may not be, depending on how the memory allocation functions work in your implementation).
If you want to sort based on the DataLine type itself, don't use a pointer. Store the objects themselves.
You can see a similar effect from the following code which creates two sets. The first is a set of integer pointers, the second a set of actual integers:
#include <iostream>
#include <iomanip>
#include <set>
using namespace std;
int main (void) {
set<int*> ipset;
set<int> iset;
cout << "inserting: ";
for (int i = 0; i < 10; i++) {
int val = (i * 7) % 13;
cout << ' ' << setw(2) << val;
ipset.insert (new int (val));
iset.insert (val);
}
cout << '\n';
cout << "integer pointer set:";
for (set<int*>::iterator it = ipset.begin(); it != ipset.end(); ++it)
cout << ' ' << setw(2) << **it;
cout << '\n';
cout << "integer set: ";
for (set<int>::iterator it = iset.begin(); it != iset.end(); ++it)
cout << ' ' << setw(2) << *it;
cout << '\n';
cout << "integer pointer set pointers:\n";
for (set<int*>::iterator it = ipset.begin(); it != ipset.end(); ++it)
cout << " " << *it << '\n';
cout << '\n';
return 0;
}
When you run that code, you see something like:
inserting: 0 7 1 8 2 9 3 10 4 11
integer pointer set: 0 7 1 8 2 9 3 10 4 11
integer set: 0 1 2 3 4 7 8 9 10 11
integer pointer set pointers:
0x907c020
0x907c060
0x907c0a0
0x907c0e0
0x907c120
0x907c160
0x907c1a0
0x907c1e0
0x907c220
0x907c260
You can see the unordered way in which values are added to the two sets (first line) and the way the pointer set in this case matches the order of input (second line). That's because the addresses are what's being used for ordering as you can see by the fact that the final section shows the ordered addresses.
Although, as mentioned, it may not necessarily match the input order, since the memory arena may be somewhat fragmented (as one example).
The set containing the actual integers (as opposed to pointers to integers) is clearly sorted by the integer value itself (third line).
You need to define member operator < like below, and save objects in std::set instead of raw pointers. Because for raw pointers, the default comparision criteria is based on the pointer value itself.
bool operator < (const DataLine &dataLine) const
{
return time < dataLine.time;
}
...
std::set<DataLine> data;

Handling an 'else' type situation within a loop

I have a homework problem for my C++ class and the problem wants us to have the user input a wavelength and then output the correct type of radiation. The point to notice is that there are more Wave Name values than there are Wave Lengths.
My solution is listed below:
const double WAVE_LENGTH[] = { 1e-11, 1e-8, 4e-7, 7e-7, 1e-3, 1e-2 };
const char* WAVE_NAME[] = { "Gamma Rays", "X Rays", "Ultraviolet", "Visible Light", "Infrared", "Microwaves", "Radio Waves" };
double waveLength;
std::cout << "Enter a wavelength in decimal or scientific notation\nWavelength: ";
std::cin >> waveLength;
for (unsigned short i = 0U; i < 6U; ++i)
{
if (waveLength < WAVE_LENGTH[i])
{
std::cout << "The type of radiation is " << WAVE_NAME[i] << std::endl;
break;
}
if (i == 5U) // Last iteration
std::cout << "The type of radiation is " << WAVE_NAME[i + 1] << std::endl;
}
My question is regarding my approach at solving the problem, specifically within the loop. I can't seem to find a way to handle all the situations without creating two conditions inside the loop which seems like it is a poor design. I realize I could use a series of if/else if statements, but I figured a loop is cleaner. Is my approach the best way or is there a cleaner way of coding this?
Thanks!
I think you can simplify your loop to this:
unsigned short i;
for (i = 0U; i < 6U; ++i)
{
if (waveLength < WAVE_LENGTH[i])
{
break;
}
}
std::cout << "The type of radiation is " << WAVE_NAME[i] << std::endl;
In my view a somewhat cleaner design is to add positive infinity as the last element of WAVE_LENGTH. This way your corner case will require no special handling:
#include <iostream>
#include <limits>
...
const double WAVE_LENGTH[] = { 1e-11, 1e-8, 4e-7, 7e-7, 1e-3, 1e-2,
std::numeric_limits<double>::infinity() };
const char* WAVE_NAME[] = { "Gamma Rays", "X Rays", "Ultraviolet", "Visible Light",
"Infrared", "Microwaves", "Radio Waves" };
double waveLength;
std::cout << "Enter a wavelength in decimal or scientific notation\nWavelength: ";
std::cin >> waveLength;
for (int i = 0; i < sizeof(WAVE_LENGTH) / sizeof(WAVE_LENGTH[0]); ++i)
{
if (waveLength < WAVE_LENGTH[i])
{
std::cout << "The type of radiation is " << WAVE_NAME[i] << std::endl;
break;
}
}
Also note how I've avoided having to hard-code the length of the array (6U in your code) in the loop's terminal condition.
You can test the last iteration in the same if. Notice there is no test anymore itn for.
for (unsigned short i = 0U; ; ++i)
{
if (i == 6 || waveLength < WAVE_LENGTH[i])
{
std::cout << "The type of radiation is " << WAVE_NAME[i] << std::endl;
break;
}
}
Alternatively, you can add a extra wavelength set to MAX_FLOAT (or whatever is called in C++) or set the last one to zero and exit if wave_length[i] == 0.0. That way you don't need to "know" the number of wave lengths.