How to custom sort a VCL TListBox? - c++

I'm trying to figure out how to supply my own custom sort method to items and strings in a TListBox.
My list box stores a custom object in its Object property, and I need to use that in the custom sort.
I'm basing the below code on this post (Delphi): Is it possible to sort a TListBox using a custom sort comparator?
My custom sort function looks like this
int __fastcall SortListByValue (TStringList* sl, int item1, int item2)
{
IniKey* k1 = (IniKey*) sl->Objects[item1];
IniKey* k2 = (IniKey*) sl->Objects[item2];
return k1->mValue < k2->mValue;
}
The key values are strings. Currently they can be "-", "Yes", "No" and "Pass".
And the code where it is called is like this:
void __fastcall TMainForm::sortByValueAExecute(TObject *Sender)
{
Log(lInfo) << "Sorting list based on Values";
TStringList* sl = new TStringList();
sl->Assign(imagesLB->Items);
sl->CustomSort(SortListByValue);
imagesLB->Items->Assign(sl);
}
The above code does "something" to the list, but its not sorted.
The resulting list starts with "-" items, and all "Yes" items are consecutive. "No" and "Pass" and "-" items are then scrambled.
Any clues?

Your sort function is expected to return a value that is < 0, 0, or > 0, depending on the desired order of the two input parameters. But you are not doing that correctly. You are returning either 0 or 1, but never < 0, because you are returning the (implicitly converted) result of a boolean expression, which can only be false or true.
You need to change this line:
return k1->mValue < k2->mValue;
To this instead:
if (k1->mValue < k2->mValue) return -1;
else if (k1->mValue > k2->mValue) return 1;
else return 0;
Alternatively, use the RTL's AnsiCompareStr() or CompareStr() function instead:
return AnsiCompareStr(k1->mValue, k2->mValue);
return CompareStr(k1->mValue, k2->mValue);

Related

Using the TList class to reorder multiple fields

int __fastcall ListSortFunc1(void *Item1, void *Item2)
{
MyStruct *item1 = (MyStruct*)Item1;
MyStruct *item2 = (MyStruct*)Item2;
return (item1->string1 < item2->string1) ? (item1->string1 > item2->string1) :
StrToInt64(item1->number1) - StrToInt64(item2->number1);
}
Reading the online documentation, it is not very clear how to use the Sort method.
My need is to reorder two or more fields. Currently, I have to reorder a file where the first field is numeric, the second is the date, the third a string, the fourth still a string.
I did some tests with Excel and with the code that it reports, but I get completely different results.
Can anyone kindly provide me with directions?
TList::Sort() is passed a callback function that is called during sorting to compare pairs of values from the list. The callback is expected to conform to the specification of the TListSortCompare type. Per its documentation:
Item1 and Item2 are 2 elements from the list. When these are passed to the TListSortCompare function, the Sort method is asking which order they should be in. The comparison returns a value determined by the relative values of Item1 and Item2, as shown in this table:
Value
Description
>0 (positive)
Item1 is greater than Item2
0
Item1 is equal to Item2
<0 (negative)
Item1 is less than Item2
Your function does not satisfy that requirement.
When item1->string1 is less than item2->string1, you are returning 0 when you should be returning a negative value.
Otherwise, you return the result of subtracting item2->number1 from item1->number1. But you are comparing the number1 fields when item1->string1 is greater than or equal to item2->string1. You should be comparing the number1 fields only when the string1 fields are equal. Also, you are risking overflows by using subtraction if the number1 fields have large values.
Try something more like this instead:
int __fastcall ListSortFunc1(void *Item1, void *Item2)
{
MyStruct *item1 = static_cast<MyStruct*>(Item1);
MyStruct *item2 = static_cast<MyStruct*>(Item2);
if (item1->string1 == item2->string1)
{
// simply subtracting the values could lead to integer overflows
// for large values, so just compare the values as-is...
// Also: why are these not stored as __int64 to begin with?
__int64 int1 = StrToInt64(item1->number1);
__int64 int2 = StrToInt64(item2->number1);
if (int1 < int2) return -1;
if (int1 > int2) return 1;
return 0;
}
else
{
return (item1->string1 < item2->string1) ? -1 : 1;
// or:
return CompareStr(item1->string1, item2->string1); // case sensitive
// or:
return CompareText(item1->string1, item2->string1); // case insensitive
}
}

Accessing a Lua table within a table from C++ side

I'm trying to transfer a table where there may be nested tables from the lua and write to a .ini file. But I just can not how I need to go on the stack to get data from nested tables. This code does not work as it should. Function setData work not correctly. What could be the problem?
C++ code
int sasl::LuaUtilities::luaWriteIni(LuaState inOutState)
{
string path;
boost::property_tree::ptree iniTree;
LUA_CHECK_NARG_EQ(inOutState, 2);
LUA_GET_PARAM(inOutState, 1, path);
int nargs = lua_gettop(inOutState);
for (int i = 1; i <= nargs; i++) {
if (lua_istable(inOutState, nargs)) {
setData(inOutState, nargs, iniTree);
}
}
return 0;
}
void sasl::LuaUtilities::setData(LuaState inOutState, int index, boost::property_tree::ptree & inIniTree)
{
// Push an initial nil to init lua_next
lua_pushnil(inOutState);
// Parse the table at index
while (lua_next(inOutState, index))
{
if (lua_istable(inOutState, index))
{
setData(inOutState, index, inIniTree);
}
else
{
string key = lua_tostring(inOutState, -2);
string value = lua_tostring(inOutState, -1);
}
// Pop value, keep key
lua_pop(inOutState, 1);
}
return;
}
Lua code
t = {}
local fileName = findResourceFile("test.ini")
t = readINI(fileName)
writeINI(fileName, t) --this not work in c++ side
There are two problems. lua_istable(inOutState, index) is wrong, because index is not the value of the key retrieved by next. That index is always the table you're iterating over. So you'll infinitely recurse over the same table when you call setData with that index.
In fact, passing index to setData itself is almost certainly wrong. Or at least, it's probably not right. You want to use relative indices here, but calling next pushes an extra value onto the stack.
What you probably want to do is have setData assume that the table to iterate over is at index -1 (ie: the top of the stack). That way, you're just calling lua_next(state, -2) (this is -2 because the key to get the next one for is at the top). And when you recursively call setData for a table value, you don't need to provide an index, because the table value is already at the top of the stack.
The second problem is that you never write the key/value pairs. You also never check to see if the value is something which can be converted to a string. It could be a Lua userdata.

How to modify every value in unordered map in c++

I have a unordered map (umap) in C++ :
unordered_map<int, bool> dTimeResetUmap;
I am setting its value like:
dTimeResetUmap[person.object_id] = true;
person.object_id can be 0, 1, 2, 3 any int number. At certain point in code, I have to modify the al the values in it (basically have to make all the values as false) which I am doing like below:
int size_of_dTimeResetUmap = dTimeResetUmap.size();
for (int i = 0; i <= size_of_dTimeResetUmap; i++)
{
dTimeResetUmap[i] = false;
}
But it seems not to be working for some value. After a long run of code, there are few values inside dTimeResetUmap which remains true instead of false. What can be the reason. Is it not a good way of updating values. Please help Thanks.
Use C++ iterations to visit each element of map:
for (auto & element : dTimeResetUmap)
{
element.second = false;
}
If you use the indexing operator [] to access a value in the map, and the key isn't in the map, then a new key-value pair will be created, with a default "zero" value.
For a bool value, this "zero" will be equal to false.
So the simplest way to set all elements to false is to just remove all elements as then all access to the non-existing keys will create false values:
dTimeResetUmap.clear();
You can use STL iterators;
for (auto it = umap.begin(); it != umap.end(); it++) {
(*it).second = false;
}

How to check if element exists in a linked list in c++?

The problem:
Imagine there is a linked list class which has multiple methods to use. The main part of the code is like this:
int main ()
{
List l;
l.push_back (86);
l.push_front (43);
l.push_front (12);
int intToSearchFor = 12;
if (l.exists (intToSearchFor))
{
cout << "(" << intToSearchFor << ") found :)";
}
else
{
cout << "(" << intToSearchFor << ") not found :(";
}
}
As you can see in this piece of code, the List class has two methods to prepend and append new items to the list. And also it has an iterator method which lets us loop over the items and check the data in each node.
The issue?
I want to create a method which check existence of an element. For example l.exists(12) should return either true or false.
My Solution:
Start to loop over the items with iterator.
Check if the item data is equal to the one you are looking for.
If it is equal then return true. Otherwise if there are more items in the list, move into the next item in the list and go to step 2.
If there are no more items in the list return false.
bool List::exists (int x)
{
Iterator it = this->get_iterator ();
do {
if (it.current->data == x) {
return true;
}
it.current = it.current->next;
} while (it.has_more_elements ());
return false;
}
Full answer:
http://cpp.sh/6cfdh
You should check whether a pointer points to nullptr before accessing data pointed by the pointer (see has_more_elements()). Better naming may avoid some confusion.
while(it.current->data!=x && it.has_more_elements())
it.current=it.current->next;
return (it.current->data==x)?true:false;
If x is not present and it reaches end of list, it.current->data will cause run time error as it may be NULL.
while(it.has_more_elements() && it.current->data!=x)
it.current = it.current->next;
return it.current!=NULL;

Sitrecore MinValue

I need to sort very large number of item in Sitecore.
So I used negative number, something like this:
item.sortOrder = int.MinValue + someId
minValue = -2147483647
but sitecore sort correctly only for value greater then -2143053648 so there is a difference of 4430001
so now my code is
item.sortOrder = int.MinValue + 4430001 + someId
and it sorts correctly
does anyone ever experienced something like this, am I doing something wrong?
I'm not sure what exactly you are trying to achieve but it would be better to define a public constant for Sitecore SortOrder Minimum value somewhere accessible.
public const int SortOrderMinimumValue = -2143053648;
And then reference like this
item.Fields["__Sortorder"].Value = SortOrderMinimumValue + someId;
That way the minimum value can be accessed any other code and can be altered at one location should you want to change the minimum value for your sorting.
To sort Items by sort order you then will need to do the following
public class ItemComparer : IComparer
{
public int CompareSortOrder(Item item1, Item item2)
{
int sortOrder1;
bool parsed1 = int.TryParse(item1["__Sortorder"], out sortOrder1);
int sortOrder2;
bool parsed2 = int.TryParse(item2["__Sortorder"], out sortOrder2);
if (!parsed1 || !parsed2)
throw new Exception("Sort order value is incorrect type");
if (sortOrder1 < sortOrder2)
return -1;
if (sortOrder1 > sortOrder2)
return 1;
else
return 0;
}
}
And then reference the code when sorting;
var itemComparer = new ItemComparer();
items.Sort(itemComparer.CompareSortOrder);