Search code examples
rebolrebol3collect

Rebol COLLECT: keep the order of the source data in a loop


There is some source data, like [1 2 3 4 "a" "b" "c" "d"], four items as a group. I want to extract some data at specific positions, such as the first, third, and fourth from each group.

There is my code:

data: [1 2 3 4 "a" "b" "c" "d"]
output: copy []
foreach [p1 p2 p3 p4] data [ collect/into [keep p1 keep p3 keep p4] output ]
probe output    ;;the output is ["a" "c" "d" 1 3 4]

But what I really want is [1 3 4 "a" "c" "d"]. How can I keep the order?


Solution

  • All functions in Rebol which use the /into option use insert semantics. We added that option to allow incremental building without needing to make as many intermediate series, and to allow you to pick the type of target series, preallocate, and all sorts of other power user tricks. The /into option uses insert semantics because insert doesn't lose as much information as append.

    Let's take your example but just use collect:

    data: [1 2 3 4 "a" "b" "c" "d"]
    output: collect [
        foreach [p1 p2 p3 p4] data [ keep p1 keep p3 keep p4 ]
    ]
    probe output
    

    That's the kind of simple code that collect is supposed to make it easier to write. But it's a bit slow, so let's try optimizing it a bit by using /into:

    data: [1 2 3 4 "a" "b" "c" "d"]
    output: copy []
    foreach [p1 p2 p3 p4] data [
        output: collect/into [keep p1 keep p3 keep p4] output
    ]
    probe head output
    

    That is the standard model for /into code, and will make things output in the order you want. But it really doesn't have any advantages over using regular collect, since you don't preallocate the target block. This will save on reallocations:

    data: [1 2 3 4 "a" "b" "c" "d"]
    output: make block! 0.75 * length? data
    foreach [p1 p2 p3 p4] data [
        output: collect/into [keep p1 keep p3 keep p4] output
    ]
    probe head output
    

    But collect itself can be a little slow because it's not native; it's mostly a convenience function. Fortunately, we have faster native functions, which use /into the same way:

    data: [1 2 3 4 "a" "b" "c" "d"]
    output: make block! 0.75 * length? data
    foreach [p1 p2 p3 p4] data [ output: reduce/into [p1 p3 p4] output ]
    probe head output
    

    No non-native functions used there, this should be pretty fast.