Question

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?

Was it helpful?

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.

OTHER TIPS

For a reason unknown to me, collect uses insert internally, so it inserts data at the beginning, not append them at the end. I hope someone can give an explanation why it's this way. Meanwhile, you can use good old repend to do the job:

data: [1 2 3 4 "a" "b" "c" "d"]
output: copy []
forskip data 4 [repend output [data/1 data/3 data/4]]
probe output    ;; [1 3 4 "a" "c" "d"]

Also, it's better to use forskip instead of foreach in this case, as you do not have to define variables and access just by index.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top