4
\$\begingroup\$

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.

\$\endgroup\$
  • \$\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 Dec 6 '14 at 19:42
  • \$\begingroup\$ @HostileFork I put my head down for a few minutes and miss something as important as that! \$\endgroup\$ – rgchris Dec 6 '14 at 21:00
3
\$\begingroup\$

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)]
    ]
]
\$\endgroup\$
  • \$\begingroup\$ I'm not opposed to SORT/COMPARE—indeed I wish this could just be written as sort/compare block-of-objects 'name—just not sure how fast/efficient it is compared to the above. Need some speed tests :) \$\endgroup\$ – rgchris Dec 18 '14 at 23:42
  • \$\begingroup\$ What are the backticks for? \$\endgroup\$ – HostileFork Dec 19 '14 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 Dec 19 '14 at 9:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.