collect

Redlang documentation | Core function

collect return a block of collected values filtered by keep. Quick example:


    block: [1 keep 2 3 keep 4 5 keep 6]
    collect block

== [2 4 6]

Usage 1: Generate a sequence without filter

Example: Generate a sequence of numbers

collect [repeat i 6 [keep i]]

will return the suite of numbers:

== [1 2 3 4 5 6]

Usage 2: Filter a list using keep

Example: collect even numbers in a sequence

keep (filter) only even numbers


    collect [repeat i 6 [if even? i [keep i]]] 
    

== [2 4 6]

Usage : Generate double sequences with keep/only


collection: collect [
	repeat i 2 [
		repeat j 2 [
			keep/only reduce [i j]
		]
	]
]
    

will output

== [[1 1] [1 2] [2 1] [2 2]]

Optional Usage: collect into a buffer with /into

Example

let's initialize an even buffer as empty block


    even: copy [] 
    

then collect into buffer:


    collect/into [repeat i 6 [if even? i [keep i]]] even
    ?? even
    

== [2 4 6]

Practical examples

list of files filtered by extension

for example, let's copy all *.png and *jpg files:


; /------------------- folders config ------------------
param>source-folder: "..\img\"
param>target-folder: "d:\projects\Red\redlang.red\upload\img\"

~source-folder: to-red-file (param>source-folder)
~target-folder: to-red-file (param>target-folder)

; ------------------- folders config ------------------/


; /------------------- filter images files ------------------
~filenames: read ~source-folder
~image-filenames: collect [
    foreach ~filename ~filenames [
        ~extension: suffix? ~filename
        if find [%.png %.gif %.jpg] ~extension [
            keep ~filename
        ]  
    ]
]
; ------------------- filter images files ------------------/


; /------------------- copy images files ------------------
foreach ~image-filename ~image-filenames [
    ~target-file: to-red-file rejoin [(param>target-folder) ~image-filename]
    ~source-file: to-red-file rejoin [(param>source-folder) ~image-filename]
    ; ?? ~source-file
    ; ?? ~target-file
    either error? try [
        write/binary ~target-file 
        read/binary ~source-file
    ][
        print ["error copying" ~source-file "to" ~target-file "check folder exists"]
    ][
        print ["copied" ~source-file "to" ~target-file]
    ]

]
; ------------------- copy images files ------------------/
   
    

in the above code, we collect files by keeping only those with png and jpeg extension.

Exclude known folders from a list of folders

exclude 2 folders %.index/ %.template/:


folders: read %./ ; for example [%.index/ %.template/ %test/]
folders: collect [
    forall folders [unless (%.index/ = folders/1) or (%.template/ = folders/1) or (not dir? folders/1) 
    [keep folders/1]] 
]
?? folders ; for example folders: [%test/]        
    

or more generically:


folders: read %./ ; get list of folders in grand grand parent folder

in-exclusion-list: [%.vscode/ %.index/ %.template/] ; we want to exclude these folders
folders: collect [
    foreach folder folders [
        not-folder?: (not dir? folder) ; it's a file not a folder so we also exclude
        in-exclusion?: (not none? find in-exclusion-list folder) ; folder not found in exclusion list
        unless (in-exclusion? or not-folder?) [keep folder] ; in these cases keep folder in the list
    ]
]        
    

Filter a list of users by age

With keep/only, you can keep brackets around user structure (otherwise you get a non structured list) for example, let get all the adults from a list of users where each user has a firstName and an age:


users: [
	[firstName: "John" age: 32]
	[firstName: "Peter" age: 15]
	[firstName: "Mary" age: 55]
]  

adults: collect [foreach user users 
    [
		if user/age >= 18 [
			keep/only user
		]
	]
]         
    

will output:

== [
    [firstName: "John" age: 32] 
    [firstName: "Mary" age: 55]
]

Aggregate file names from multiple directories with collect/keep and compose

For example let's aggregate %../build/ and %../ci/ folders with collect/keep and compose:


files: collect [
    keep compose [(read %../build/) (read %../ci/)]
]
?? files
    

This will output for example:

files: [%assemble.red %build.red %production.config %production.red %release.red]

Aggregate file paths from multiple directories with collect/keep and compose

For example let's aggregate the same %../build/ and %../ci/ folders, you have to nest collect within collect with compose, insert and clean-path (see function compostion):


files: collect [
    keep compose [
        (collect [foreach file read %../build/ [keep head insert file clean-path %../build/ ]]) 
        (collect [foreach file read %../ci/ [keep head insert file clean-path %../ci/]]) 
    ]
]
?? files
    

This will output for example:

files: [%/d/projects/Red/redlang.red/src/cache.inc/build/assemble.red %/d/projects/Red/redlang.red/src/cache.inc/build/build.red %/d/projects/Red/redlang.red/src/cache.inc/ci/production.config %/d/projects/Red/redlang.red/src/cache.inc/ci/production.red %/d/projects/Red/redlang.red/src/cache.inc/ci/release.red]

Note: if you need to exclude folder type, you can use:

unless dir? file [...]

so code would be:


files: collect [
    keep compose [
        (collect [foreach file read %../build/ [unless dir? file [keep head insert file clean-path %../build/] ]]) 
        (collect [foreach file read %../ci/ [unless dir? file [keep head insert file clean-path %../ci/] ]) 
    ]
]
    

Learn side by side | for javascript developers

generate and filter a sequence of even numbers

Redlang | Javascript:

Javascript:


// generate and filter a sequence of even numbers

let arr = Array(6).fill().map((_, idx) => idx + 1);
let even = arr.filter(val => {return val % 2 === 0;});
console.log(even); // [ 2, 4, 6 ]        
    

Redlang:


Red [Title: "generate and filter a sequence of even numbers"]

even: collect [repeat i 6 [if even? i [keep i]]] 
?? even ; even: [2 4 6]        
    

filter a list of users by age

Redlang | Javascript:

Javascript:


const users = [
  { firstName: "John", age: 32 },
  { firstName: "Peter", age: 15 }, 
  { firstName: "Mary", age: 55 },
]
const filter = (callback, list) => list.filter(callback);
const map = (callback, list) => list.map(callback);

adults = map(user => user, filter(user => user.age >= 18, users));
console.log(adults); // [ { firstName: 'John', age: 32 }, { firstName: 'Mary', age: 55 } ]   
    

Redlang:

users: [
	[firstName: "John" age: 32]
	[firstName: "Peter" age: 15]
	[firstName: "Mary" age: 55]
]  

adults: collect [foreach user users 
    [
		if user/age >= 18 [
			keep/only user
		]
	]
]

References

official documentation:

collect and parse | Red-lang.org