How to create a custom sort for strings based on a logical order?

Task - Assign a ComboBox it’s values based on a custom logical order.

I’m pulling in Strings from an xml and need them to order in the comboBox in a custom way.

EX: ComboBox values are Advanced Photo, High Quality, High Speed, Ultra-Fine Photo. I would like the comboBox to fill it’s values in that order as well. (AP - 0, HQ - 1, HS - 2, UFP - 3)

Below is where the sort code needs to go.

for (int i = 0; i < speeds.size(); i++) {
speedComboBox->addItem(speeds[i], i + 1);
if (savedValue.compare(speeds[i]) == 0) {
found = true;
speedComboBox->setSelectedItemIndex(i, sendNotification);
}
}

Are you asking how to do this? Or are you just asking someone else to write code for you? In any case, assuming the list you posted is what will be the data, a) you would want to presort the speeds before adding them to the combobox, for simplicity sake. and b) you would want to use some String functions to locate the dash (-) in the speed strings, and then sort by the remain portion of the string. or, if you want to be more robust (and support values that exceed a single digit, ie. 10+), after locating the dash, you would convert the remainder of the string to an integer, and sort by that value. This is because sort numerical string values does not work the same as comparing actual numbers.

I’m asking how to do this, not writing code. Does juce have a custom sort that I can easily implement?

What is the container type for speeds?

StringArray,

StringArray, has a publicly accessible Array<String> strings, and Array has some sort function available:

template<class ElementComparator >
int 	addSorted (ElementComparator &comparator, ParameterType newElement)
 	Inserts a new element into the array, assuming that the array is sorted. More...
 
void 	addUsingDefaultSort (ParameterType newElement)
 	Inserts a new element into the array, assuming that the array is sorted. More...
 
template<typename ElementComparator , typename TargetValueType >
int 	indexOfSorted (ElementComparator &comparator, TargetValueType elementToLookFor) const
 	Finds the index of an element in the array, assuming that the array is sorted. More...

Should be as easy as writing the proper ElementComparator and using addSorted

Guess i’m getting stumped at giving numerical/order value to strings and how to compare them. This isn’t really related to juce though.

I quickly explained it. Use string functions to locate the dash in those strings, then from the point just after the dash, you convert the string to a number, and do the compare. Here is untested code I am just typing in at the moment

// curString would contain the element being processed at the moment (AP - 0, HQ - 1, etc)
const auto idParsedFrromString { curString.fromFirstOccuranceOf ("-", false, true).trimStart ().getIntValue () };

Oh sorry, I was just trying to explain the order of values.

The strings will come in as.

Advanced Photo
High Quality
High Speed
Ultra-Fine Quality

The only hyphen will be in ultra. There will be no numbers.

oh man… lol… so I have no idea what you are asking to do? how do you want to sort them? just by their names? so, to be clear, what is the data coming from the xml? what are the sort criteria? if the sort criteria is not part of the data read in, then the fact that it is read in, is meaningless, because you have to hardcode the sorting, so the values could just as easily be hardcoded into the app.

So I have a comboBox that needs to be populated in a custom order.

There are 4 possible values.

This is the order.

  1. High Speed, 2. High quality, 3. Advanced Photo 4. Ultra-fine Photo

High speed should be at index value 0 in the comboBox WHEN it’s available and so on.

All these speeds aren’t always available, so there must be a custom sort of available speeds. The speeds are dependable on other values in the program, so not all 4 are always available.

The data is coming in as strings in a stringarray.

untested, but something like this, where you pass in the list from the xml you read into the function:

const auto allQualities {std::pair<String,int>
{
    {"High Speed", 1},
	{"High quality", 2},
	{"Advanced Photo", 3},
	{"Ultra-fine Photo", 4}
};

void MyClass::populateComboBox(StringArray availableQaulities)
{
    comboBox.clear ();
	for (const auto [quality, id] : allQualities)
	    if (availableQaulities.contains(quality))
		    comboBox.addItem(quality, id);
}

Thanks, I will test and mess around with that. However, won’t the comboBox have blank values if you hardcode the 1-4 index placement. I.E If there’s only high speed and utlrafine, index 2 and 3 will be empty blank space?

Thanks again.

The strings in the ComboBox will remain in the order as they were added. The ID is arbitrary, it just cannot be 0. You may set any ID to any item according to your likings.

2 Likes

Id’s and Indcies are different things in a combobox. The index IS the position value, the first item is idex 0, the second 1, etc… ID’s are arbitrary (non-0) values that are stored along with each item. It is super useful to store a value that is required for that setting, but differs from indexing. In this case, we use the ID to specify the quality setting, regardless of how many items are in the list.

2 Likes

Dumb question,

const auto allQualities {std::pair<String,int>
{
{“High Speed”, 1},
{“High quality”, 2},
{“Advanced Photo”, 3},
{“Ultra-fine Photo”, 4}
};

Is not compiling, “C++ no instance of constructor matches the argument list argument types are: ({…}, {…}, {…}, {…})”. I thought it was a semantic error but can’t get it to compile. Can you point towards some documentation for this?

I believe you’re trying to create some a list of pairs. Going to try and see if that works.

Not at all stupid, it is not the simplest of declarations. As I said, I hadn’t tested, but now that I have (at least at the compilation level, here is the correct code for that:

const auto allQualities { std::vector<std::pair<String,int>>
{
    {"High Speed", 1},
    {"High quality", 2},
    {"Advanced Photo", 3},
    {"Ultra-fine Photo", 4}
} };

Could you please give more description on what you’re doing here?

const auto allQualities { std::vector<std::pair<String,int>>
{
{“High Speed”, 1},
{“High quality”, 2},
{“Advanced Photo”, 3},
{“Ultra-fine Photo”, 4}
} };

You’re declaring/initializing a vector of pairs but I don’t see a name for the pairs. I’m just not used to this declaration.

Anyway it worked but I also need to be able to access the current index as I iterate for something else.

I found this: ```
for (auto& elem: v) {
// if the current index is needed:
auto i = &elem - &v[0];

// any code including continue, break, return

}


but it is not working.

I guess i'm also asking, why is there no name for the element in this for each loop?

for (const auto  [quality, id] : allQualities)

Well I just declared an int i before the for loop and i++ as I went through, that seemed to work but doesn’t seem very clean.

I’ve given you as much time as I can on this. it would behoove to learn more about c++. Best of luck. Hopefully someone else can step in and help you understand the details.

1 Like