[Closed] sort array problem
--hey,guys! I have a problem like this:
--create 120 spheres
for i=1 to 120 do (sphere radius:(random 4 10) pos:(random [-100.0,-100.0,-100.0] [100.0,100.0,100.0]))
--after select some of them and collect their names
spherename=for i in selection collect i.name
--sometime result like this in ascending order:
--#("Sphere09", "Sphere10", "Sphere11", "Sphere12", "Sphere13","Sphere100", "Sphere101", "Sphere102", "Sphere103", "Sphere104")
--sometime the result is not in ascending order,so I use sort on it
sort spherename
--alas! I got result like this
--#("Sphere09", "Sphere10", "Sphere100", "Sphere101", "Sphere102", "Sphere103", "Sphere104","Sphere10", "Sphere11", "Sphere12", "Sphere13")
--anybody help me get an ascending order,thanks!
For your examle you can try something like this
delete objects
speresArr = for i=1 to 120 collect (sphere radius:(random 4 10) pos:(random [-100.0,-100.0,-100.0] [100.0,100.0,100.0]))
handleArr = sort (for i = 1 to 10 collect getHandleByAnim speresArr[random 1 120])
sphereNames = for i in handleArr collect (getAnimByHandle i).name
it calls ‘natural numeric sort’. try to find a solution on the internet. there are many examples.
.net has a built-in solution. and you can use .net with mxs
You could also name the nodes with a consistent number of digits to solve the problems sorting the array.
In Max 2011+ you can use "uniqueName" function or "nameSuffixLength" property.
uniqueName <prefix> [numDigits:<integer>]
maxOps.nameSuffixLength
In versions previous to 2011 you could use “formattedPrint” to rename the nodes using a given number of digits. In the following example spheres are renamed to “Sphere001”, “Sphere002” and so on:
for i = 1 to 120 do
(
sphere radius:(random 4 10) \
pos:(random [-100,-100,-100] [100,100,100]) \
name:("Sphere" + (formattedprint i format:"03i"))
)
THANK Branko ivković,
THANK Jorge Rodríguez,
Denis Trofimov you like a teacher,give me a clue and help me solve my trouble by myself,I believe this is a good way to learning. but please remember google can’t use freely in my country.
well… let’s do the natural sort using pure MXS…
i will show just how the things work, and i will try to do it simple and clear
first of all is about the problem. try to sort the built-in way:
maxOps.nameSuffixLength = 1
for k=1 to 1000 do box()
nn = for obj in objects collect obj.name
sort nn
you can see that higher indexed objects are going before lower.
i want to have them as 1,2,3,etc.
the step #1.
#1 to learn how to split the numerated name on two parts: base and index.
the easiest and most ‘economic’ method i found is:
fn splitNumeratedName name =
(
base = trimright name "0123456789"
id = substring name (base.count+1) -1
#(base, id)
)
#2 make a qsort function using a split method:
fn naturalSort name1 name2 =
(
base1 = trimright name1 "0123456789"
base2 = trimright name2 "0123456789"
if (act = stricmp base1 base2) != 0 then act else
(
id1 = substring name1 (base1.count+1) -1
id2 = substring name2 (base2.count+1) -1
id1 as integer - id2 as integer
)
)
#3 just sort it with qsort:
qsort nn naturalSort
where is a problem(s) ?
it works well and fast enough for 1000 objects but let’s do it for 20000:
delete objects
for k=1 to 20000 do box()
nn = for obj in objects collect obj.name
and sort:
t1 = timestamp()
m1 = heapfree
qsort nn namuralSort
format "time:% memory:%
" (timestamp() - t1) (m1 - heapfree)
we can probably leave with the speed (~2.5 sec on my machine) but the memory usage is too big.
this is a general problem for all mxs string methods. MXS doesn’t do in-place string operations. every time it makes a new instance of a string object. that’s way all string operations are expensive in mxs, and why you have to avoid using of them where it possible.
now is an ‘after lesson’ question:
- how can we improve the sorting method shown above to make it faster and more memory friendly?
My try
fn splitNumeratedName name =
(
string = trimright name "0123456789"
number = substring name (string.count+1) -1
#(string, number)
)
fn naturalSort arr1 arr2 =
(
if (act = stricmp arr1[1] arr2[1]) != 0 then act else (arr1[2] as integer - arr2[2] as integer)
)
delete objects
nn = for i = 1 to 20000 collect
(
node = box()
splitNumeratedName node.name
)
qsort nn naturalSort