Difference between revisions of "LlListSortStrided"

From Second Life Wiki
Jump to navigation Jump to search
(Added a caveat that the sort is not stable)
m (s/sort/sorting algorithm/)
 
(2 intermediate revisions by the same user not shown)
Line 33: Line 33:
* When strings contain numbers, the numbers are still sorted left-to-right like any other character, which may not necessarily match numeric order: <source lang="lsl2">llListSortStrided(["127", "3", "25"], 1, 0, TRUE) // returns ["127", "25", "3"] because the 1 in 127 is before the 2 in 25 which is before the 3</source> To sort them in numeric order, numbers in strings can be padded with zeros: <source lang="lsl2">llListSortStrided(["127", "003", "025"], 1, 0, TRUE) // returns ["003", "025", "127"]</source>
* When strings contain numbers, the numbers are still sorted left-to-right like any other character, which may not necessarily match numeric order: <source lang="lsl2">llListSortStrided(["127", "3", "25"], 1, 0, TRUE) // returns ["127", "25", "3"] because the 1 in 127 is before the 2 in 25 which is before the 3</source> To sort them in numeric order, numbers in strings can be padded with zeros: <source lang="lsl2">llListSortStrided(["127", "003", "025"], 1, 0, TRUE) // returns ["003", "025", "127"]</source>
** This order differs from the order of items in a prim's inventory, which is "natural order" (e.g "New Script 2" is sorted before "New Script 11").
** This order differs from the order of items in a prim's inventory, which is "natural order" (e.g "New Script 2" is sorted before "New Script 11").
* The sort is not stable; if the sort key matches, the input list's order is not preserved. Thus you cannot use 2 sorts on different columns to sort by multiple keys: <source lang="lsl2">
* The sorting algorithm is not [https://en.wikipedia.org/wiki/Sorting_algorithm#Stability stable]. Thus you cannot use 2 sorts on different columns to sort by multiple keys: <source lang="lsl2">
default { // THIS DOES NOT WORK AS A MULTI-KEY SORT
default { state_entry() { // THIS DOES NOT WORK AS A MULTI-KEY SORT
     state_entry() {
     list input = [1, "b", 0, "c", 1, "a", 0, "b", 1, "c", 0, "a"];
        list input = [1, "b", 0, "c", 1, "a", 0, "b", 1, "c", 0, "a"];
    list partialSort = llListSortStrided(input, 2, 1, TRUE);
        list partialSort = llListSortStrided(input, 2, 1, TRUE);
    llOwnerSay("partialSort = " + llList2Json(JSON_ARRAY, partialSort));
        llOwnerSay("partialSort = " + llList2Json(JSON_ARRAY, partialSort));
    list fullSort = llListSortStrided(partialSort, 2, 0, TRUE);
        list fullSort = llListSortStrided(partialSort, 2, 0, TRUE);
    llOwnerSay("fullSort = " + llList2Json(JSON_ARRAY, fullSort));
        llOwnerSay("fullSort = " + llList2Json(JSON_ARRAY, fullSort));
} }
    }
}
</source>This gives the following output (errors bolded):
</source>This gives the following output (errors bolded):
** partialSort = [1,"a",0,"a",'''0,'''"b",'''1,'''"b",'''1,'''"c",'''0,'''"c"]
** partialSort = [1,"a",0,"a",'''0,'''"b",'''1,'''"b",'''1,'''"c",'''0,'''"c"]

Latest revision as of 10:12, 7 October 2024

Summary

Function: list llListSortStrided( list src, integer stride, integer stride_index, integer ascending );
0.0 Forced Delay
O(N2) Compl.
10.0 Energy

llListSortStrided is llListSort with the added parameter of stride_index, adding the flexibility to sort by any item in the stride. These routines use the same underlying code and have the same computational complexity.
Returns a list that is src sorted by the stride_index item in every stride.

• list src List to be sorted.
• integer stride number of entries per stride, if less than 1 it is assumed to be 1
• integer stride_index The index within the stride to sort by. stride_index is 0-indexed. The first element is 0, second 1, etc. An index of 0 is functionally identical to using llListSort. Negative indexes count from the end of the stride, e.g. -1 means the last element in the stride.
• integer ascending if TRUE then the sort order is ascending, otherwise the order is descending.

This function supports Strided Lists.

Specification

The sort order is affected by type. For strings and keys, it is case sensitive and sorts by Unicode character code.

llListSortStrided(["a", "á", "B", "C", "d", "e"], 1, 0, TRUE) // returns ["B", "C", "a", "d", "e", "á"]

For ascending sort, each type is sorted individually and then feathered to have the same order of types.

llListSortStrided([1, "C", 3, "A", 2, "B"], 1, 0, TRUE) // returns [1, "A", 2, "B", 3, "C"]

llListSortStrided([1, 3, 2, "C", "A", "B"], 1, 0, TRUE) // returns [1, 2, 3, "A", "B", "C"]

llListSortStrided([1, "C", 3, "A", 2, "B"], 2, 0, TRUE) // returns [1, "C", 2, "B", 3, "A"]

llListSortStrided([1, "C", 3, "A", 2, "B"], 2, 1, TRUE) // returns [3, "A", 2, "B", 1, "C"]

Caveats

  • It uses the same unoptimized selection sort algorithm as llListSort, which is an algorithm with a Big O of N². A JIRA issue exists to improve this function, SVC-2988.
  • Originally the wiki stated that non-zero values for the "ascending" parameter would produce an ascending sort. That was incorrect. For this function, the value must be exactly 1 (or TRUE) for an ascending sort.
  • Vectors are sorted by magnitude. SVC-5643
  • Rotations are not sorted in any meaningful order. If a list containing only rotations is sorted in ascending order, it will be returned unchanged.
  • For descending sort, if there are mixed types, the final order is deterministic (the same input will always produce the same output) but it can be completely useless.
    llListSortStrided([2, "B", "C", 3, 1, "A"], 1, 0, FALSE) // returns ["A", 3, 1, "C", "B", 2]
    
    If there are no mixed types, however, the descending sort works just fine.
  • When the stride is greater than 1, if the list length is not a multiple of the stride, the list will be returned unchanged.
  • stride_index must be less than stride and greater than or equal to -stride, otherwise an empty list is returned.
  • When strings contain numbers, the numbers are still sorted left-to-right like any other character, which may not necessarily match numeric order:
    llListSortStrided(["127", "3", "25"], 1, 0, TRUE) // returns ["127", "25", "3"] because the 1 in 127 is before the 2 in 25 which is before the 3
    
    To sort them in numeric order, numbers in strings can be padded with zeros:
    llListSortStrided(["127", "003", "025"], 1, 0, TRUE) // returns ["003", "025", "127"]
    
    • This order differs from the order of items in a prim's inventory, which is "natural order" (e.g "New Script 2" is sorted before "New Script 11").
  • The sorting algorithm is not stable. Thus you cannot use 2 sorts on different columns to sort by multiple keys:
    default { state_entry() { // THIS DOES NOT WORK AS A MULTI-KEY SORT
        list input = [1, "b", 0, "c", 1, "a", 0, "b", 1, "c", 0, "a"];
        list partialSort = llListSortStrided(input, 2, 1, TRUE);
        llOwnerSay("partialSort = " + llList2Json(JSON_ARRAY, partialSort));
        list fullSort = llListSortStrided(partialSort, 2, 0, TRUE);
        llOwnerSay("fullSort = " + llList2Json(JSON_ARRAY, fullSort));
    } }
    
    This gives the following output (errors bolded):
    • partialSort = [1,"a",0,"a",0,"b",1,"b",1,"c",0,"c"]
    • fullSort = [0,"a",0,"b",0,"c",1,"b",1,"c",1,"a"]

Examples

list score_board = ["Awesome", "Resident", 200, "Star", "Marxman", 999, "Happy2", "Survive", 1];
default
{
    state_entry()
    {
        llOwnerSay("Unsorted: " + llDumpList2String(score_board, ","));
        score_board = llListSortStrided(scoreboard, 3, 0, TRUE);
        llOwnerSay("Sort by first names: " + llDumpList2String(numbers, ","));
        // Object: Sort by first names: Awesome,Resident,200,Happy2,Survive,1,Star,Marxman,999

        score_board = llListSortStrided(scoreboard, 3, 1, TRUE);
        llOwnerSay("Sort by last names: " + llDumpList2String(numbers, ","));
        // Object: Sort by first names: Star,Marxman,999,Awesome,Resident,200,Happy2,Survive,1

        score_board = llListSortStrided(scoreboard, 3, 2, TRUE);
        llOwnerSay("Sort by last names: " + llDumpList2String(numbers, ","));
        // Object: Sort by first names: ,Happy2,Survive,1,Awesome,Resident,200,Star,Marxman,999

    }
}

llListSort and llListSortStrided really only works on items of the same type. It will work on lists that hold diverse data types -- to be clear, it won't blow up your script -- but the results returned are usually meaningless.

list mylist = ["brown", <0.000000, 0.000000, 0.000000>, "house", 17.005, 100, "cat", <3.000000, 3.000000, 3.000000>, 39];
list tmplist = llListSortStrided(mylist, 1, 0, TRUE);
llSay(0, llList2CSV(tmplist));

This returns in chat:

brown, <0.000000, 0.000000, 0.000000>, cat, 17.004999, 39, house, <3.000000, 3.000000, 3.000000>, 100

The same ordered in descending order returns even more meaningless results:

list mylist = ["brown", <0.000000, 0.000000, 0.000000>, "house", 17.005, 100, "cat", <3.000000, 3.000000, 3.000000>, 39];
list tmplist = llListSortStrided(mylist, 1, 0, FALSE);
llSay(0, llList2CSV(tmplist));

returns in chat:

39, <3.000000, 3.000000, 3.000000>, cat, 100, 17.004999, house, <0.000000, 0.000000, 0.000000>, brown

Utilizing the Results

It's important to note that the source list that you are sorting will remain unchanged. Instead, a new, sorted list will be produced. So, it's important that you capture this with a variable (unless you are acting directly on the results.)

llListSortStrided(myList, 1, 0, TRUE); // You've wasted cpu time; you didn't capture the results

list newlist = llListSortStrided(myList, 1, 0, TRUE);// Okay. You've captured the results.

llSay(0,llList2CSV(llListSortStrided(myList, 1, 0, TRUE))); // No need to capture, using the results right away.

Stride parameter

Most times, you will want to set "integer stride" to 1 (0 also works) to tell it to sort each item in the list on its own basis. (If you are working with a strided list, though, see the special section below on sorting strides.)

Sort Order

Setting the parameter "integer ascending" to TRUE returns a sorted list that is in ascending order.
For example: ["Apples", "Bananas", "Oranges"]

Setting the parameter "integer ascending" to FALSE returns a sorted list that is in descending order.
For example: ["Oranges", "Bananas", "Apples"]

Sorting Strided Lists

If you have a strided list, in which you are keeping related pieces of data together in chunks, letting each list element sort on its own basis would be disastrous.

list demographics = ["John Adams", "male", "2007-06-22", "Shirley Bassey", "female", "2005-11-02", "Matt Damon", "male", "2008-05-19"];

Bad Example

list tmplist_1 = llListSortStrided(demographics, 1, 0, TRUE);
//tmplist_1 == ["2005-11-02", "2007-06-22", "2008-05-19", "John Adams", "Matt Damon", "Shirley Bassey", "female", "male", "male"]
//The strides have been destroyed, the sorted data is now useless

Good Example

Instead, because you have the data grouped (aka "strided") in sets of 3, you need to do this:

list tmplist_2 = llListSortStrided(demographics, 3, 0, TRUE);
//templist_2 = ["John Adams", "male", "2007-06-22", "Matt Damon", "male", "2008-05-19", "Shirley Bassey", "female", "2005-11-02"]

Deep Notes

Signature

function list llListSortStrided( list src, integer stride, integer stride_index, integer ascending );