I'm looking for a more consise way to sort a block of objects. Where you can sort a block of blocks based on the numeric position of the values to compare, there's no such shorthand for comparing objects by a given field:
test-data: reduce [
object [name: "A" value: 1]
object [name: "B" value: 3]
object [name: "C" value: 2]
]
One solution would be to extend the native SORT
function to accept a WORD!
argument corresponding to the object field. However, that would require an update to the language and might take a little while to be accepted.
I've sketched out the following SORT-BY
function that would take either a word (for the object field) or a block (an expression that is applied to the object):
sort-by: func [series [block!] comparator [block! word!]][
forskip series 2 [
insert series either word? comparator [
all [
in series/1 :comparator
get in series/1 :comparator
]
][
use [object] compose [
object: first series
(comparator)
]
]
]
sort/skip series 2
head forall series [remove series]
]
It's not ideal—it iterates through the block pulling the respective value from each object and adding it to the block; sorts the block; and removes the value again.
sort-by test-data 'value
sort-by test-data 'name
sort-by test-data [object/value]
sort-by test-data [sine 50 * object/value]
Any thoughts on the approach, particularly economy? Or even the need for such a function...
Obviously is lacking a descending/ascending switch—implied in future revision.
-
\$\begingroup\$ "However, that would require an update to the language and is unlikely to be applied to Rebol 2 anyhow." There's a new drop of Rebol2 being made, 64-bit and such, you might mention it. :-) \$\endgroup\$HostileFork says dont trust SE– HostileFork says dont trust SE2014年12月06日 19:42:03 +00:00Commented Dec 6, 2014 at 19:42
-
\$\begingroup\$ @HostileFork I put my head down for a few minutes and miss something as important as that! \$\endgroup\$rgchris– rgchris2014年12月06日 21:00:06 +00:00Commented Dec 6, 2014 at 21:00
1 Answer 1
Any reason you can't use the SORTs /COMPARE refinement here?
Here is a working example of sort-by
that uses it:
sort-by: function [
series [block!]
comparator [block! word!]
][
sort/compare series func [`a `b] either word? comparator [
[`a/:comparator < `b/:comparator]
][
sortie: function [by] [
s: to-paren copy/deep comparator
forall s [
if all [path? s/1 s/1/1 = 'object] [s/1/1: by]
]
s
]
compose [(sortie '`a) < (sortie '`b)]
]
]
-
\$\begingroup\$ I'm not opposed to
SORT/COMPARE
—indeed I wish this could just be written assort/compare block-of-objects 'name
—just not sure how fast/efficient it is compared to the above. Need some speed tests :) \$\endgroup\$rgchris– rgchris2014年12月18日 23:42:55 +00:00Commented Dec 18, 2014 at 23:42 -
\$\begingroup\$ What are the backticks for? \$\endgroup\$HostileFork says dont trust SE– HostileFork says dont trust SE2014年12月19日 02:32:41 +00:00Commented Dec 19, 2014 at 2:32
-
\$\begingroup\$ @HostileFork Oh I put them in there to (help) avoid any possible name collisions (with func parameters) in something like
a: 50 sort-by test-data [sine a * object/value]
. Didn't test so perhaps not needed? \$\endgroup\$draegtun– draegtun2014年12月19日 09:33:47 +00:00Commented Dec 19, 2014 at 9:33