2019-04-20 00:02:17 +00:00
//////////////////////////////////////////////////////////////////////
// LibFile: arrays.scad
// List and Array manipulation functions.
// To use, add the following lines to the beginning of your file:
// ```
// use <BOSL2/std.scad>
// ```
//////////////////////////////////////////////////////////////////////
// Section: Terminology
// - **List**: An ordered collection of zero or more items. ie: ["a", "b", "c"]
// - **Vector**: A list of numbers. ie: [4, 5, 6]
// - **Array**: A nested list of lists, or list of lists of lists, or deeper. ie: [[2,3], [4,5], [6,7]]
// - **Dimension**: The depth of nesting of lists in an array. A List is 1D. A list of lists is 2D. etc.
// Section: List Operations
// Function: replist()
// Usage:
// replist(val, n)
// Description:
// Generates a list or array of `n` copies of the given `list`.
// If the count `n` is given as a list of counts, then this creates a
// multi-dimensional array, filled with `val`.
// Arguments:
// val = The value to repeat to make the list or array.
// n = The number of copies to make of `val`.
// Example:
// replist(1, 4); // Returns [1,1,1,1]
// replist(8, [2,3]); // Returns [[8,8,8], [8,8,8]]
// replist(0, [2,2,3]); // Returns [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]
// replist([1,2,3],3); // Returns [[1,2,3], [1,2,3], [1,2,3]]
function replist ( val , n , i = 0 ) =
2019-05-27 05:34:46 +00:00
is_num ( n ) ? [ for ( j = [ 1 : 1 : n ] ) val ] :
2019-04-20 00:02:17 +00:00
( i >= len ( n ) ) ? val :
2019-05-27 05:34:46 +00:00
[ for ( j = [ 1 : 1 : n [ i ] ] ) replist ( val , n , i + 1 ) ] ;
2019-04-20 00:02:17 +00:00
// Function: in_list()
// Description: Returns true if value `x` is in list `l`.
// Arguments:
// x = The value to search for.
// l = The list to search.
// idx = If given, searches the given subindexes for matches for `x`.
// Example:
// in_list("bar", ["foo", "bar", "baz"]); // Returns true.
// in_list("bee", ["foo", "bar", "baz"]); // Returns false.
// in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true.
function in_list ( x , l , idx = undef ) = search ( [ x ] , l , num_returns_per_match = 1 , index_col_num = idx ) ! = [ [ ] ] ;
// Function: slice()
// Description:
// Returns a slice of a list. The first item is index 0.
// Negative indexes are counted back from the end. The last item is -1.
// Arguments:
// arr = The array/list to get the slice of.
// st = The index of the first item to return.
// end = The index after the last item to return, unless negative, in which case the last item to return.
// Example:
// slice([3,4,5,6,7,8,9], 3, 5); // Returns [6,7]
// slice([3,4,5,6,7,8,9], 2, -1); // Returns [5,6,7,8,9]
// slice([3,4,5,6,7,8,9], 1, 1); // Returns []
// slice([3,4,5,6,7,8,9], 6, -1); // Returns [9]
// slice([3,4,5,6,7,8,9], 2, -2); // Returns [5,6,7,8]
function slice ( arr , st , end ) = let (
s = st < 0 ? ( len ( arr ) + st ) : st ,
e = end < 0 ? ( len ( arr ) + end + 1 ) : end
2019-05-27 05:34:46 +00:00
) [ for ( i = [ s : 1 : e - 1 ] ) if ( e > s ) arr [ i ] ] ;
2019-04-20 00:02:17 +00:00
// Function: select()
// Description:
// Returns a portion of a list, wrapping around past the beginning, if end<start.
// The first item is index 0. Negative indexes are counted back from the end.
// The last item is -1. If only the `start` index is given, returns just the value
// at that position.
// Usage:
// select(list,start)
// select(list,start,end)
// Arguments:
// list = The list to get the portion of.
// start = The index of the first item.
// end = The index of the last item.
// Example:
// l = [3,4,5,6,7,8,9];
// select(l, 5, 6); // Returns [8,9]
// select(l, 5, 8); // Returns [8,9,3,4]
// select(l, 5, 2); // Returns [8,9,3,4,5]
// select(l, -3, -1); // Returns [7,8,9]
// select(l, 3, 3); // Returns [6]
// select(l, 4); // Returns 7
// select(l, -2); // Returns 8
// select(l, [1:3]); // Returns [4,5,6]
// select(l, [1,3]); // Returns [4,6]
function select ( list , start , end = undef ) =
let ( l = len ( list ) )
end = = undef ? (
is_num ( start ) ?
let ( s = ( start % l + l ) % l ) list [ s ] :
[ for ( i = start ) list [ ( i % l + l ) % l ] ]
) : (
let ( s = ( start % l + l ) % l , e = ( end % l + l ) % l )
( s < = e ) ?
2019-05-27 05:34:46 +00:00
[ for ( i = [ s : 1 : e ] ) list [ i ] ] :
concat ( [ for ( i = [ s : 1 : l - 1 ] ) list [ i ] ] , [ for ( i = [ 0 : 1 : e ] ) list [ i ] ] )
2019-04-20 00:02:17 +00:00
) ;
// Function: list_range()
// Usage:
// list_range(n, [s], [e], [step])
// list_range(e, [step])
// list_range(s, e, [step])
// Description:
// Returns a list, counting up from starting value `s`, by `step` increments,
// until either `n` values are in the list, or it reaches the end value `e`.
// Arguments:
// n = Desired number of values in returned list, if given.
// s = Starting value. Default: 0
// e = Ending value to stop at, if given.
// step = Amount to increment each value. Default: 1
// Example:
// list_range(4); // Returns [0,1,2,3]
// list_range(n=4, step=2); // Returns [0,2,4,6]
// list_range(n=4, s=3, step=3); // Returns [3,6,9,12]
2019-06-19 08:51:47 +00:00
// list_range(n=5, s=0, e=10); // Returns [0, 2.5, 5, 7.5, 10]
2019-04-20 00:02:17 +00:00
// list_range(e=3); // Returns [0,1,2,3]
// list_range(e=6, step=2); // Returns [0,2,4,6]
// list_range(s=3, e=5); // Returns [3,4,5]
// list_range(s=3, e=8, step=2); // Returns [3,5,7]
// list_range(s=4, e=8, step=2); // Returns [4,6,8]
// list_range(n=4, s=[3,4], step=[2,3]); // Returns [[3,4], [5,7], [7,10], [9,13]]
function list_range ( n = undef , s = 0 , e = undef , step = 1 ) =
2019-06-19 08:51:47 +00:00
( n ! = undef && e ! = undef ) ? [ for ( i = [ 0 : 1 : n - 1 ] ) s + ( e - s ) * i / ( n - 1 ) ] :
2019-05-27 05:34:46 +00:00
( n ! = undef ) ? [ for ( i = [ 0 : 1 : n - 1 ] ) let ( v = s + step * i ) v ] :
2019-04-20 00:02:17 +00:00
( e ! = undef ) ? [ for ( v = [ s : step : e ] ) v ] :
assert ( e ! = undef || n ! = undef , "Must supply one of `n` or `e`." ) ;
// Function: reverse()
// Description: Reverses a list/array.
// Arguments:
// list = The list to reverse.
// Example:
// reverse([3,4,5,6]); // Returns [6,5,4,3]
function reverse ( list ) = [ for ( i = [ len ( list ) - 1 : - 1 : 0 ] ) list [ i ] ] ;
2019-06-17 06:54:44 +00:00
// Function: deduplicate()
// Usage:
// deduplicate(list);
// Description:
// Removes consecutive duplicate items in a list.
// This is different from `unique()` in that the list is *not* sorted.
// Arguments:
// list = The list to deduplicate.
// eps = The maximum difference to allow between numbers or vectors.
// Examples:
// deduplicate([8,3,4,4,4,8,2,3,3,7]); // Returns: [8,3,4,8,2,3,7]
// deduplicate("Hello"); // Returns: ["H","e","l","o"]
// deduplicate([[3,4],[7,2],[7,1.99],[1,4]],eps=0.1); // Returns: [[3,4],[7,2],[1,4]]
function deduplicate ( list , eps = EPSILON ) =
( is_num ( list [ 0 ] ) || is_vector ( list [ 0 ] ) ) ?
[ for ( i = [ 0 : 1 : len ( list ) - 1 ] ) if ( ! approx ( list [ i ] , list [ i + 1 ] , eps ) ) list [ i ] ] :
[ for ( i = [ 0 : 1 : len ( list ) - 1 ] ) if ( list [ i ] ! = list [ i + 1 ] ) list [ i ] ] ;
2019-06-12 02:26:06 +00:00
// Function: list_set()
// Usage:
// list_set(indices, values, list, [dflt], [minlen])
// Description:
// Takes the input list and returns a new list such that `list[indices[i]] = values[i]` for all of
// the (index,value) pairs supplied. If you supply `indices` that are beyond the length of the list
// then the list is extended and filled in with the `dflt` value. If you set `minlen` then the list is
// lengthed, if necessary, by padding with `dflt` to that length. The `indices` list can be in any
// order but run time will be (much) faster for long lists if it is already sorted. Reptitions are
// not allowed.
// Arguments:
// indices = List of indices into `list` to set.
// values = List of values to set.
// list = List to set items in.
// dflt = Default value to store in sparse skipped indices.
// minlen = Minimum length to expand list to.
function list_set ( indices , values , list = [ ] , dflt = 0 , minlen = 0 ) =
! is_list ( indices ) ? list_set ( list , [ indices ] , [ values ] , dflt ) :
assert ( len ( indices ) = = len ( values ) , "Index list and value list must have the same length" )
let (
sortind = list_increasing ( indices ) ? list_range ( len ( indices ) ) : sortidx ( indices ) ,
lastind = indices [ select ( sortind , - 1 ) ]
)
concat (
[ for ( j = [ 0 : 1 : indices [ sortind [ 0 ] ] - 1 ] ) j >= len ( list ) ? dflt : list [ j ] ] ,
[ values [ sortind [ 0 ] ] ] ,
[ for ( i = [ 1 : 1 : len ( sortind ) - 1 ] ) each
assert ( indices [ sortind [ i ] ] ! = indices [ sortind [ i - 1 ] ] , "Repeated index" )
concat (
[ for ( j = [ 1 + indices [ sortind [ i - 1 ] ] : 1 : indices [ sortind [ i ] ] - 1 ] ) j >= len ( list ) ? dflt : list [ j ] ] ,
[ values [ sortind [ i ] ] ]
)
] ,
slice ( list , 1 + lastind , len ( list ) ) ,
replist ( dflt , minlen - lastind - 1 )
) ;
2019-04-20 00:02:17 +00:00
// Function: list_remove()
// Usage:
// list_remove(list, elements)
// Description:
// Remove all items from `list` whose indexes are in `elements`.
// Arguments:
// list = The list to remove items from.
// elements = The list of indexes of items to remove.
2019-06-12 02:26:06 +00:00
function list_remove ( list , elements ) =
! is_list ( elements ) ? list_remove ( list , [ elements ] ) :
let ( sortind = list_increasing ( elements ) ? list_range ( len ( elements ) ) : sortidx ( elements ) ,
lastind = elements [ select ( sortind , - 1 ) ]
)
assert ( lastind < len ( list ) , "Element index beyond list end" )
concat ( slice ( list , 0 , elements [ sortind [ 0 ] ] ) ,
[ for ( i = [ 1 : 1 : len ( sortind ) - 1 ] ) each slice ( list , 1 + elements [ sortind [ i - 1 ] ] , elements [ sortind [ i ] ] ) ] ,
slice ( list , 1 + lastind , len ( list ) )
) ;
2019-04-20 00:02:17 +00:00
// Function: list_insert()
// Usage:
// list_insert(list, pos, elements);
// Description:
// Insert `elements` into `list` before position `pos`.
function list_insert ( list , pos , elements ) =
concat (
slice ( list , 0 , pos ) ,
elements ,
2019-05-27 05:34:46 +00:00
( pos < len ( list ) ? slice ( list , pos , - 1 ) : [ ] )
2019-04-20 00:02:17 +00:00
) ;
2019-06-26 00:44:21 +00:00
// Function: bselect()
// Usage:
// bselect(array,index);
// Description:
// Returns the items in `array` whose matching element in `index` is true.
// Arguments:
// array = Initial list to extract items from.
// index = List of booleans.
// Example:
// bselect([3,4,5,6,7], [false,true,true,false,true]); // Returns: [4,5,7]
function bselect ( array , index ) =
[ for ( i = [ 0 : len ( array ) - 1 ] ) if ( index [ i ] ) array [ i ] ] ;
// Function: list_bset()
// Usage:
// list_bset(indexset, valuelist)
// Description:
// Opposite of `bselect()`. Returns a list the same length as `indexlist`, where each item will
// either be 0 if the corresponding item in `indexset` is false, or the next sequential value
// from `valuelist` if true. The number of `true` values in `indexset` must be equal to the length
// of `valuelist`.
// Arguments:
// indexset = A list of boolean values.
// valuelist = The list of values to set into the returned list.
// dflt = Default value to store when the indexset item is false.
// Example:
// list_bset([false,true,false,true,false], [3,4]); // Returns: [0,3,0,4,0]
function list_bset ( indexset , valuelist , dflt = 0 ) =
let (
trueind = search ( [ true ] , indexset , 0 ) [ 0 ]
) concat (
list_set ( trueind , valuelist , dflt = dflt ) , // Fill in all of the values
replist ( dflt , len ( indexset ) - max ( trueind ) - 1 ) // Add trailing values so length matches indexset
) ;
2019-06-22 17:33:49 +00:00
// Function: list_increasing()
// Usage:
// list_increasing(list)
2019-06-26 00:44:21 +00:00
// Description:
// Returns true if the list is (non-strictly) increasing
2019-06-12 02:26:06 +00:00
function list_increasing ( list , ind = 0 ) = ind < len ( list ) - 1 && list [ ind ] < = list [ ind + 1 ] ? list_increasing ( list , ind + 1 ) :
( ind >= len ( list ) - 1 ? true : false ) ;
2019-06-22 17:33:49 +00:00
// Function: list_decreasing()
// Usage:
// list_increasing(list)
// Description: returns true if the list is (non-strictly) decreasing
2019-06-12 02:26:06 +00:00
function list_decreasing ( list , ind = 0 ) = ind < len ( list ) - 1 && list [ ind ] >= list [ ind + 1 ] ? list_increasing ( list , ind + 1 ) :
( ind >= len ( list ) - 1 ? true : false ) ;
2019-04-20 00:02:17 +00:00
// Function: list_shortest()
// Description:
// Returns the length of the shortest sublist in a list of lists.
// Arguments:
// vecs = A list of lists.
function list_shortest ( vecs ) =
min ( [ for ( v = vecs ) len ( v ) ] ) ;
// Function: list_longest()
// Description:
// Returns the length of the longest sublist in a list of lists.
// Arguments:
// vecs = A list of lists.
function list_longest ( vecs ) =
max ( [ for ( v = vecs ) len ( v ) ] ) ;
// Function: list_pad()
// Description:
// If the list `v` is shorter than `minlen` length, pad it to length with the value given in `fill`.
// Arguments:
// v = A list.
// minlen = The minimum length to pad the list to.
// fill = The value to pad the list with.
function list_pad ( v , minlen , fill = undef ) =
2019-05-27 06:18:49 +00:00
concat ( v , replist ( fill , minlen - len ( v ) ) ) ;
2019-04-20 00:02:17 +00:00
// Function: list_trim()
// Description:
// If the list `v` is longer than `maxlen` length, truncates it to be `maxlen` items long.
// Arguments:
// v = A list.
// minlen = The minimum length to pad the list to.
function list_trim ( v , maxlen ) =
2019-05-27 06:18:49 +00:00
[ for ( i = [ 0 : 1 : min ( len ( v ) , maxlen ) - 1 ] ) v [ i ] ] ;
2019-04-20 00:02:17 +00:00
// Function: list_fit()
// Description:
// If the list `v` is longer than `length` items long, truncates it to be exactly `length` items long.
// If the list `v` is shorter than `length` items long, pad it to length with the value given in `fill`.
// Arguments:
// v = A list.
// minlen = The minimum length to pad the list to.
// fill = The value to pad the list with.
function list_fit ( v , length , fill ) =
let ( l = len ( v ) ) ( l = = length ) ? v : ( l > length ) ? list_trim ( v , length ) : list_pad ( v , length , fill ) ;
// Function: enumerate()
// Description:
// Returns a list, with each item of the given list `l` numbered in a sublist.
// Something like: `[[0,l[0]], [1,l[1]], [2,l[2]], ...]`
// Arguments:
// l = List to enumerate.
// idx = If given, enumerates just the given subindex items of `l`.
// Example:
// enumerate(["a","b","c"]); // Returns: [[0,"a"], [1,"b"], [2,"c"]]
// enumerate([[88,"a"],[76,"b"],[21,"c"]], idx=1); // Returns: [[0,"a"], [1,"b"], [2,"c"]]
// enumerate([["cat","a",12],["dog","b",10],["log","c",14]], idx=[1:2]); // Returns: [[0,"a",12], [1,"b",10], [2,"c",14]]
function enumerate ( l , idx = undef ) =
( idx = = undef ) ?
2019-05-27 05:34:46 +00:00
[ for ( i = [ 0 : 1 : len ( l ) - 1 ] ) [ i , l [ i ] ] ] :
[ for ( i = [ 0 : 1 : len ( l ) - 1 ] ) concat ( [ i ] , [ for ( j = idx ) l [ i ] [ j ] ] ) ] ;
2019-04-20 00:02:17 +00:00
2019-05-30 00:42:09 +00:00
// Function: shuffle(list)
// Description:
// Shuffles the input list into random order.
function shuffle ( list ) =
len ( list ) < = 1 ? list :
let (
rval = rands ( 0 , 1 , len ( list ) ) ,
left = [ for ( i = [ 0 : len ( list ) - 1 ] ) if ( rval [ i ] < 0.5 ) list [ i ] ] ,
right = [ for ( i = [ 0 : len ( list ) - 1 ] ) if ( rval [ i ] >= 0.5 ) list [ i ] ]
) concat ( shuffle ( left ) , shuffle ( right ) ) ;
2019-06-25 23:16:40 +00:00
// Sort a vector of scalar values
function _sort_scalars ( arr ) =
len ( arr ) < = 1 ? arr :
let ( pivot = arr [ floor ( len ( arr ) / 2 ) ] ,
lesser = [ for ( y = arr ) if ( y < pivot ) y ] ,
equal = [ for ( y = arr ) if ( y = = pivot ) y ] ,
greater = [ for ( y = arr ) if ( y > pivot ) y ]
)
concat ( _sort_scalars ( lesser ) , equal , _sort_scalars ( greater ) ) ;
// Sort a vector of vectors based on the first entry only of each vector
function _sort_vectors1 ( arr ) =
len ( arr ) < = 1 ? arr :
! ( len ( arr ) > 0 ) ? [ ] :
let ( pivot = arr [ floor ( len ( arr ) / 2 ) ] ,
lesser = [ for ( y = arr ) if ( y [ 0 ] < pivot [ 0 ] ) y ] ,
equal = [ for ( y = arr ) if ( y [ 0 ] = = pivot [ 0 ] ) y ] ,
greater = [ for ( y = arr ) if ( y [ 0 ] > pivot [ 0 ] ) y ]
)
concat ( _sort_vectors1 ( lesser ) , equal , _sort_vectors1 ( greater ) ) ;
// Sort a vector of vectors based on the first two entries of each vector
// Lexicographic order, remaining entries of vector ignored
function _sort_vectors2 ( arr ) =
len ( arr ) < = 1 ? arr :
! ( len ( arr ) > 0 ) ? [ ] :
let ( pivot = arr [ floor ( len ( arr ) / 2 ) ] ,
lesser = [ for ( y = arr ) if ( y [ 0 ] < pivot [ 0 ] || ( y [ 0 ] = = pivot [ 0 ] && y [ 1 ] < pivot [ 1 ] ) ) y ] ,
equal = [ for ( y = arr ) if ( y [ 0 ] = = pivot [ 0 ] && y [ 1 ] = = pivot [ 1 ] ) y ] ,
greater = [ for ( y = arr ) if ( y [ 0 ] > pivot [ 0 ] || ( y [ 0 ] = = pivot [ 0 ] && y [ 1 ] > pivot [ 1 ] ) ) y ]
)
concat ( _sort_vectors2 ( lesser ) , equal , _sort_vectors2 ( greater ) ) ;
// Sort a vector of vectors based on the first three entries of each vector
// Lexicographic order, remaining entries of vector ignored
function _sort_vectors3 ( arr ) =
len ( arr ) < = 1 ? arr :
let ( pivot = arr [ floor ( len ( arr ) / 2 ) ] ,
lesser = [ for ( y = arr ) if ( y [ 0 ] < pivot [ 0 ] ||
( y [ 0 ] = = pivot [ 0 ] && ( y [ 1 ] < pivot [ 1 ] ||
( y [ 1 ] = = pivot [ 1 ] && y [ 2 ] < pivot [ 2 ] ) ) ) ) y ] ,
equal = [ for ( y = arr ) if ( y [ 0 ] = = pivot [ 0 ] && y [ 1 ] = = pivot [ 1 ] && y [ 2 ] = = pivot [ 2 ] ) y ] ,
greater = [ for ( y = arr ) if ( y [ 0 ] > pivot [ 0 ] ||
( y [ 0 ] = = pivot [ 0 ] && ( y [ 1 ] > pivot [ 1 ] ||
( y [ 1 ] = = pivot [ 1 ] && y [ 2 ] > pivot [ 2 ] ) ) ) ) y ]
)
concat ( _sort_vectors3 ( lesser ) , equal , _sort_vectors3 ( greater ) ) ;
// Sort a vector of vectors based on the first four entries of each vector
// Lexicographic order, remaining entries of vector ignored
function _sort_vectors4 ( arr ) =
len ( arr ) < = 1 ? arr :
let ( pivot = arr [ floor ( len ( arr ) / 2 ) ] ,
lesser = [ for ( y = arr ) if ( y [ 0 ] < pivot [ 0 ] ||
( y [ 0 ] = = pivot [ 0 ] && ( y [ 1 ] < pivot [ 1 ] ||
( y [ 1 ] = = pivot [ 1 ] && ( y [ 2 ] < pivot [ 2 ] ||
( y [ 2 ] = = pivot [ 2 ] && y [ 3 ] < pivot [ 3 ] ) ) ) ) ) ) y ] ,
equal = [ for ( y = arr ) if ( y [ 0 ] = = pivot [ 0 ] && y [ 1 ] = = pivot [ 1 ] && y [ 2 ] = = pivot [ 2 ] && y [ 3 ] = = pivot [ 3 ] ) y ] ,
greater = [ for ( y = arr ) if ( y [ 0 ] > pivot [ 0 ] ||
( y [ 0 ] = = pivot [ 0 ] && ( y [ 1 ] > pivot [ 1 ] ||
( y [ 1 ] = = pivot [ 1 ] && ( y [ 2 ] > pivot [ 2 ] ||
( y [ 2 ] = = pivot [ 2 ] && y [ 3 ] > pivot [ 3 ] ) ) ) ) ) ) y ]
)
concat ( _sort_vectors4 ( lesser ) , equal , _sort_vectors4 ( greater ) ) ;
function _sort_general ( arr , idx = undef ) =
2019-04-20 00:02:17 +00:00
( len ( arr ) < = 1 ) ? arr :
let (
pivot = arr [ floor ( len ( arr ) / 2 ) ] ,
pivotval = idx = = undef ? pivot : [ for ( i = idx ) pivot [ i ] ] ,
compare = [
for ( entry = arr ) let (
val = idx = = undef ? entry : [ for ( i = idx ) entry [ i ] ] ,
cmp = compare_vals ( val , pivotval )
) cmp
] ,
2019-05-27 05:34:46 +00:00
lesser = [ for ( i = [ 0 : 1 : len ( arr ) - 1 ] ) if ( compare [ i ] < 0 ) arr [ i ] ] ,
equal = [ for ( i = [ 0 : 1 : len ( arr ) - 1 ] ) if ( compare [ i ] = = 0 ) arr [ i ] ] ,
greater = [ for ( i = [ 0 : 1 : len ( arr ) - 1 ] ) if ( compare [ i ] > 0 ) arr [ i ] ]
2019-04-20 00:02:17 +00:00
)
2019-06-25 23:16:40 +00:00
concat ( _sort_general ( lesser , idx ) , equal , _sort_general ( greater , idx ) ) ;
// Function: sort()
// Usage:
// sort(list, [idx])
// Description:
// Sorts the given list using `compare_vals()`, sorting in lexicographic order, with types ordered according to
// `undef < boolean < number < string < list`. Comparison of lists is recursive.
// If the list is a list of vectors whose length is from 1 to 4 and the `idx` parameter is not passed, then
// `sort` uses a much more efficient method for comparisons and will run much faster. In this case, all entries
// in the data are compared using the native comparison operator, so comparisons between types will fail.
// Arguments:
// list = The list to sort.
// idx = If given, do the comparison based just on the specified index, range or list of indices.
// Example:
// l = [45,2,16,37,8,3,9,23,89,12,34];
// sorted = sort(l); // Returns [2,3,8,9,12,16,23,34,37,45,89]
function sort ( list , idx = undef ) =
! is_list ( list ) || len ( list ) < = 1 ? list :
is_def ( idx ) ? _sort_general ( list , idx ) :
let ( size = array_dim ( list ) )
len ( size ) = = 1 ? _sort_scalars ( list ) :
len ( size ) = = 2 && size [ 1 ] < = 4 ? (
size [ 1 ] = = 0 ? list :
size [ 1 ] = = 1 ? _sort_vectors1 ( list ) :
size [ 1 ] = = 2 ? _sort_vectors2 ( list ) :
size [ 1 ] = = 3 ? _sort_vectors3 ( list ) :
/*size[1]==4*/ _sort_vectors4 ( list )
) :
_sort_general ( list ) ;
2019-04-20 00:02:17 +00:00
// Function: sortidx()
// Description:
// Given a list, calculates the sort order of the list, and returns
// a list of indexes into the original list in that sorted order.
// If you iterate the returned list in order, and use the list items
// to index into the original list, you will be iterating the original
// values in sorted order.
// Example:
// lst = ["d","b","e","c"];
// idxs = sortidx(lst); // Returns: [1,3,0,2]
2019-06-25 23:16:40 +00:00
// ordered = select(lst, idxs); // Returns: ["b", "c", "d", "e"]
2019-04-20 00:02:17 +00:00
// Example:
// lst = [
// ["foo", 88, [0,0,1], false],
// ["bar", 90, [0,1,0], true],
// ["baz", 89, [1,0,0], false],
// ["qux", 23, [1,1,1], true]
// ];
// idxs1 = sortidx(lst, idx=1); // Returns: [3,0,2,1]
// idxs2 = sortidx(lst, idx=0); // Returns: [1,2,0,3]
// idxs3 = sortidx(lst, idx=[1,3]); // Returns: [3,0,2,1]
2019-06-25 23:16:40 +00:00
function sortidx ( list , idx = undef ) =
list = = [ ] ? [ ] :
2019-04-20 00:02:17 +00:00
let (
2019-06-25 23:16:40 +00:00
size = array_dim ( list ) ,
aug = is_undef ( idx ) && ( len ( size ) = = 1 || ( len ( size ) = = 2 && size [ 1 ] < = 4 ) ) ? zip ( list , list_range ( len ( list ) ) ) :
enumerate ( list , idx = idx )
)
is_undef ( idx ) && len ( size ) = = 1 ? subindex ( _sort_vectors1 ( aug ) , 1 ) :
is_undef ( idx ) && len ( size ) = = 2 && size [ 1 ] < = 4 ? (
size [ 1 ] = = 0 ? list_range ( len ( arr ) ) :
size [ 1 ] = = 1 ? subindex ( _sort_vectors1 ( aug ) , 1 ) :
size [ 1 ] = = 2 ? subindex ( _sort_vectors2 ( aug ) , 2 ) :
size [ 1 ] = = 3 ? subindex ( _sort_vectors3 ( aug ) , 3 ) :
/*size[1]==4*/ subindex ( _sort_vectors4 ( aug ) , 4 )
) :
// general case
subindex ( _sort_general ( aug , idx = list_range ( s = 1 , n = len ( aug ) - 1 ) ) , 0 ) ;
2019-04-20 00:02:17 +00:00
// Function: unique()
// Usage:
// unique(arr);
// Description:
// Returns a sorted list with all repeated items removed.
// Arguments:
// arr = The list to uniquify.
function unique ( arr ) =
len ( arr ) < = 1 ? arr : let (
sorted = sort ( arr )
) [
2019-05-27 05:34:46 +00:00
for ( i = [ 0 : 1 : len ( sorted ) - 1 ] )
2019-04-20 00:02:17 +00:00
if ( i = = 0 || ( sorted [ i ] ! = sorted [ i - 1 ] ) )
sorted [ i ]
] ;
// Section: Array Manipulation
2019-05-10 09:33:44 +00:00
// Function: subindex()
2019-04-20 00:02:17 +00:00
// Description:
// For each array item, return the indexed subitem.
// Returns a list of the values of each vector at the specfied
// index list or range. If the index list or range has
// only one entry the output list is flattened.
// Arguments:
// v = The given list of lists.
// idx = The index, list of indices, or range of indices to fetch.
// Example:
// v = [[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]];
2019-05-10 09:33:44 +00:00
// subindex(v,2); // Returns [3, 7, 11, 15]
// subindex(v,[2,1]); // Returns [[3, 2], [7, 6], [11, 10], [15, 14]]
// subindex(v,[1:3]); // Returns [[2, 3, 4], [6, 7, 8], [10, 11, 12], [14, 15, 16]]
function subindex ( v , idx ) = [
2019-04-20 00:02:17 +00:00
for ( val = v ) let ( value = [ for ( i = idx ) val [ i ] ] )
len ( value ) = = 1 ? value [ 0 ] : value
] ;
2019-05-10 09:33:44 +00:00
// Function: pair()
2019-04-20 00:02:17 +00:00
// Usage:
2019-05-10 09:33:44 +00:00
// pair(v)
// Description:
2019-06-19 08:31:44 +00:00
// Takes a list, and returns a list of adjacent pairs from it.
2019-05-10 09:33:44 +00:00
// Example:
// l = ["A","B","C",D"];
// echo([for (p=pair(l)) str(p.y,p.x)]); // Outputs: ["BA", "CB", "DC"]
2019-05-27 05:34:46 +00:00
function pair ( v ) = [ for ( i = [ 0 : 1 : len ( v ) - 2 ] ) [ v [ i ] , v [ i + 1 ] ] ] ;
2019-05-10 09:33:44 +00:00
// Function: pair_wrap()
// Usage:
// pair_wrap(v)
// Description:
2019-06-19 08:31:44 +00:00
// Takes a list, and returns a list of adjacent pairss from it, wrapping around from the end to the start of the list.
2019-05-10 09:33:44 +00:00
// Example:
2019-06-19 08:31:44 +00:00
// l = ["A","B","C","D"];
2019-05-10 09:33:44 +00:00
// echo([for (p=pair_wrap(l)) str(p.y,p.x)]); // Outputs: ["BA", "CB", "DC", "AD"]
2019-05-27 05:34:46 +00:00
function pair_wrap ( v ) = [ for ( i = [ 0 : 1 : len ( v ) - 1 ] ) [ v [ i ] , v [ ( i + 1 ) % len ( v ) ] ] ] ;
2019-05-10 09:33:44 +00:00
2019-06-19 08:31:44 +00:00
// Function: triplet()
// Usage:
// triplet(v)
// Description:
// Takes a list, and returns a list of adjacent triplets from it.
// Example:
// l = ["A","B","C","D","E"];
// echo([for (p=triplet(l)) str(p.z,p.y,p.x)]); // Outputs: ["CBA", "DCB", "EDC"]
function triplet ( v ) = [ for ( i = [ 0 : 1 : len ( v ) - 3 ] ) [ v [ i ] , v [ i + 1 ] , v [ i + 2 ] ] ] ;
// Function: triplet_wrap()
// Usage:
// triplet_wrap(v)
// Description:
// Takes a list, and returns a list of adjacent triplets from it, wrapping around from the end to the start of the list.
// Example:
// l = ["A","B","C","D"];
// echo([for (p=triplet_wrap(l)) str(p.z,p.y,p.x)]); // Outputs: ["CBA", "DCB", "ADC", "BAD"]
function triplet_wrap ( v ) = [ for ( i = [ 0 : 1 : len ( v ) - 1 ] ) [ v [ i ] , v [ ( i + 1 ) % len ( v ) ] , v [ ( i + 2 ) % len ( v ) ] ] ] ;
2019-05-10 09:33:44 +00:00
// Function: zip()
// Usage:
// zip(v1, v2, v3, [fit], [fill]);
// zip(vecs, [fit], [fill]);
2019-04-20 00:02:17 +00:00
// Description:
// Zips together corresponding items from two or more lists.
// Returns a list of lists, where each sublist contains corresponding
// items from each of the input lists. `[[A1, B1, C1], [A2, B2, C2], ...]`
// Arguments:
// vecs = A list of two or more lists to zipper together.
// fit = If `fit=="short"`, the zips together up to the length of the shortest list in vecs. If `fit=="long"`, then pads all lists to the length of the longest, using the value in `fill`. If `fit==false`, then requires all lists to be the same length. Default: false.
// fill = The default value to fill in with if one or more lists if short. Default: undef
// Example:
// v1 = [1,2,3,4];
// v2 = [5,6,7];
// v3 = [8,9,10,11];
2019-05-10 09:33:44 +00:00
// zip(v1,v3); // returns [[1,8], [2,9], [3,10], [4,11]]
// zip([v1,v3]); // returns [[1,8], [2,9], [3,10], [4,11]]
// zip([v1,v2], fit="short"); // returns [[1,5], [2,6], [3,7]]
// zip([v1,v2], fit="long"); // returns [[1,5], [2,6], [3,7], [4,undef]]
// zip([v1,v2], fit="long, fill=0); // returns [[1,5], [2,6], [3,7], [4,0]]
// zip([v1,v2,v3], fit="long"); // returns [[1,5,8], [2,6,9], [3,7,10], [4,undef,11]]
2019-04-20 00:02:17 +00:00
// Example:
// v1 = [[1,2,3], [4,5,6], [7,8,9]];
// v2 = [[20,19,18], [17,16,15], [14,13,12]];
2019-05-10 09:33:44 +00:00
// zip(v1,v2); // Returns [[1,2,3,20,19,18], [4,5,6,17,16,15], [7,8,9,14,13,12]]
function zip ( vecs , v2 , v3 , fit = false , fill = undef ) =
( v3 ! = undef ) ? zip ( [ vecs , v2 , v3 ] , fit = fit , fill = fill ) :
( v2 ! = undef ) ? zip ( [ vecs , v2 ] , fit = fit , fill = fill ) :
2019-04-20 00:02:17 +00:00
let (
dummy1 = assert_in_list ( "fit" , fit , [ false , "short" , "long" ] ) ,
minlen = list_shortest ( vecs ) ,
maxlen = list_longest ( vecs ) ,
dummy2 = ( fit = = false ) ? assert ( minlen = = maxlen , "Input vectors must have the same length" ) : 0
) ( fit = = "long" ) ?
2019-05-27 05:34:46 +00:00
[ for ( i = [ 0 : 1 : maxlen - 1 ] ) [ for ( v = vecs ) for ( x = ( i < len ( v ) ? v [ i ] : ( fill = = undef ) ? [ fill ] : fill ) ) x ] ] :
[ for ( i = [ 0 : 1 : minlen - 1 ] ) [ for ( v = vecs ) for ( x = v [ i ] ) x ] ] ;
2019-04-20 00:02:17 +00:00
// Function: array_group()
// Description:
// Takes a flat array of values, and groups items in sets of `cnt` length.
// The opposite of this is `flatten()`.
// Arguments:
// v = The list of items to group.
// cnt = The number of items to put in each grouping.
// dflt = The default value to fill in with is the list is not a multiple of `cnt` items long.
// Example:
// v = [1,2,3,4,5,6];
// array_group(v,2) returns [[1,2], [3,4], [5,6]]
// array_group(v,3) returns [[1,2,3], [4,5,6]]
// array_group(v,4,0) returns [[1,2,3,4], [5,6,0,0]]
2019-05-27 05:34:46 +00:00
function array_group ( v , cnt = 2 , dflt = 0 ) = [ for ( i = [ 0 : cnt : len ( v ) - 1 ] ) [ for ( j = [ 0 : 1 : cnt - 1 ] ) default ( v [ i + j ] , dflt ) ] ] ;
2019-04-20 00:02:17 +00:00
// Function: flatten()
// Description: Takes a list of lists and flattens it by one level.
// Arguments:
// l = List to flatten.
// Example:
// flatten([[1,2,3], [4,5,[6,7,8]]]) returns [1,2,3,4,5,[6,7,8]]
2019-06-22 21:00:12 +00:00
function flatten ( l ) = [ for ( a = l ) each a ] ;
2019-04-20 00:02:17 +00:00
// Internal. Not exposed.
function _array_dim_recurse ( v ) =
! is_list ( v [ 0 ] ) ? (
sum ( [ for ( entry = v ) is_list ( entry ) ? 1 : 0 ] ) = = 0 ? [ ] : [ undef ]
) : let (
firstlen = len ( v [ 0 ] ) ,
first = sum ( [ for ( entry = v ) len ( entry ) = = firstlen ? 0 : 1 ] ) = = 0 ? firstlen : undef ,
leveldown = flatten ( v )
) is_list ( leveldown [ 0 ] ) ? (
concat ( [ first ] , _array_dim_recurse ( leveldown ) )
) : [ first ] ;
// Function: array_dim()
// Usage:
// array_dim(v, [depth])
// Description:
// Returns the size of a multi-dimensional array. Returns a list of
// dimension lengths. The length of `v` is the dimension `0`. The
// length of the items in `v` is dimension `1`. The length of the
// items in the items in `v` is dimension `2`, etc. For each dimension,
// if the length of items at that depth is inconsistent, `undef` will
// be returned. If no items of that dimension depth exist, `0` is
// returned. Otherwise, the consistent length of items in that
// dimensional depth is returned.
// Arguments:
// v = Array to get dimensions of.
// depth = Dimension to get size of. If not given, returns a list of dimension lengths.
// Examples:
// array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]); // Returns [2,2,3]
// array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0); // Returns 2
// array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2); // Returns 3
// array_dim([[[1,2,3],[4,5,6]],[[7,8,9]]]); // Returns [2,undef,3]
function array_dim ( v , depth = undef ) =
( depth = = undef ) ? (
concat ( [ len ( v ) ] , _array_dim_recurse ( v ) )
) : ( depth = = 0 ) ? (
len ( v )
) : (
let ( dimlist = _array_dim_recurse ( v ) )
( depth > len ( dimlist ) ) ? 0 : dimlist [ depth - 1 ]
) ;
2019-05-12 19:32:03 +00:00
// Function: transpose()
// Description: Returns the transposition of the given array.
// Example:
// arr = [
// ["a", "b", "c"],
// ["d", "e", "f"],
// ["g", "h", "i"]
// ];
// t = transpose(arr);
// // Returns:
// // [
// // ["a", "d", "g"],
// // ["b", "e", "h"],
// // ["c", "f", "i"],
// // ]
// Example:
2019-05-12 19:54:09 +00:00
// arr = [
// ["a", "b", "c"],
// ["d", "e", "f"]
// ];
// t = transpose(arr);
// // Returns:
// // [
// // ["a", "d"],
// // ["b", "e"],
// // ["c", "f"],
// // ]
// Example:
// transpose([3,4,5]); // Returns: [3,4,5]
2019-05-12 19:32:03 +00:00
function transpose ( arr ) =
2019-05-27 05:34:46 +00:00
is_list ( arr [ 0 ] ) ? [ for ( i = [ 0 : 1 : len ( arr [ 0 ] ) - 1 ] ) [ for ( j = [ 0 : 1 : len ( arr ) - 1 ] ) arr [ j ] [ i ] ] ] : arr ;
2019-05-12 19:32:03 +00:00
2019-04-20 00:02:17 +00:00
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap