Lodash - collections

About

js.component provides several types of functions which operate on collections:

  • Equivalents to native Collection functions such as .every() and .some() that take a formula instead of a method name in v19 R5.

  • Equivalents to native Collection functions such as .indexOf() that allow you to specify case/diacritical matching of strings.

  • Additional utilities that enhance the utility of collections.

The functions below are available via the _ or lodash methods.

API

.$at()

.$at(collection : Collection) : Object

Returns an object that acts as a kind of proxy for collection with much more efficient execution of .at(). In cases where you want to use .at() multiple times with the same collection (for example in a loop), this function is preferable.

This function returns an object with the following properties:

PropertyTypeDescription
.atFormulaCalls .at() on collection
.$Collectioncollection

Examples

$c:=_.$at(New collection("a"; "b"; "c"))
$c.at(1)  // "b"
$c.at(4)  // undefined
$c.at(-1) // "c"
$c.$[1]   // "b"
$c.$.indexOf("b")  // 1

.at()

.at(collection : Collection; index : Integer) : any

Returns the element at the specified index if it exists, otherwise returns undefined. This is especially useful when used with _.equal() and the ||, && or ? operators to avoid errors when accessing an element that does not exist.

index may be a negative number, in which case it specifies an element from the end of collection. If collection.length+index is less than 0, undefined is returned.

Examples

$collection:=New collection("a"; "b"; "c")
_.at($collection; 1)  // "b"
_.at($collection; 4)  // undefined
_.at($collection; -1) // "c"
_.at($collection; -4) // undefined

// These are equivalent, but the second is preferable and more concise:
$element:=_.at($collection; 4) ? $collection[4] : "not found" // "not found"
$element:=_.at($collection; 4) || "not found"

If (_.equal(_.at($collection; 4); "c") && _.equal(_.at($collection; 1); "b"))
  // ...
End if
function foo(collection, index) {
  return _.at(collection, index) || "not found";
}

.difference()

.difference(subtractFrom : Collection; subtract : Collection { ; strict : Boolean; propertyPath : Text }) : Collection

Calculates the difference of subtractFrom and subtract and returns the result as a new, non-shared collection. The result is a shallow copy of subtractFrom’s items.

If strict is true, string collection item comparisons are case/diacritical-sensitive.

If the values in the collections are objects, then propertyPath may be passed to specify a path to the values to compare.


.distinct()

.distinct(collection : Collection; option : Integer) : Collection
.distinct(collection : Collection; propertyPath : Text { ; option : Integer }) : Collection

Like Collection.distinct()open in new window, but returns an unordered collection. Duplicate items are removed from later in the collection.

Example

$c:=New collection(3; 2; 1; 2; 1; 2)
$c.distinct() // => [1, 2, 3]
_.distinct($c) // => [3, 2, 1]

.every()

.every(collection : Collection; predicate : Callable { ; …param : any }) : Boolean
.every(collection : Collection; startFrom : Integer ; predicate : Callable { ; …param : any }) : Boolean

This is the same as Collection.every()open in new window, but allows you to use a Callable instead of a global method in v19 R5, and also provides a more flexible syntax for predicate.

predicate receives the same parameters that a method/function called via Collection.every()open in new window would. The predicate may:

  • Return a boolean
  • Set $1.result or $1.stop
  • Return a new object with .result and/or .stop

WARNING

If you are setting $1.result/stop directly, avoid nesting the call to this function in another Callable, but rather use an intermediate variable for the result of this function.

Examples

These three ways of calling .every() are equivalent:

$c:=New collection(7; 13; 27)
$result:=_.every($c; Formula($1.value<30))
$result:=_.every($c; Formula($1.result:=$1.value<30))
$result:=_.every($c; Formula(New object("result"; $1.value<30)))
// $result = True

.filter()

.filter(collection : Collection; predicate : Callable { ; …param : any }) : Collection

This is the same as Collection.filter()open in new window, but allows you to use a Callable instead of a global method in v19 R5, and also provides a more flexible syntax for predicate.

predicate receives the same parameters that a method/function called via Collection.filter()open in new window would. The predicate may:

  • Return a boolean
  • Set $1.result or $1.stop
  • Return a new object with .result and/or .stop

WARNING

If you are setting $1.result/stop directly, avoid nesting the call to this function in another Callable, but rather use an intermediate variable for the result of this function.

Examples

These three ways of calling .filter() are equivalent:

$c:=New collection(7; 13; 27)
$result:=_.filter($c; Formula($1.value<20))
$result:=_.filter($c; Formula($1.result:=$1.value<20))
$result:=_.filter($c; Formula(New object("result"; $1.value<20)))
// $result = [7, 13]

.find()

.find(collection : Collection; predicate : Callable { ; …param : any } ) : any
.find(collection : Collection; startFrom : Integer ; predicate : Callable { ; …param : any } ) : any

This is the same as Collection.find()open in new window, but allows you to use a Callable instead of a global method in v19 R5, and also provides a more flexible syntax for predicate.

NOTE

Time and Picture values cannot be searched for.

predicate receives the same parameters that a method/function called via Collection.find()open in new window would. The predicate may:

  • Return a boolean
  • Set $1.result or $1.stop
  • Return a new object with .result and/or .stop

WARNING

If you are setting $1.result/stop directly, avoid nesting the call to this function in another Callable, but rather use an intermediate variable for the result of this function.

Examples

These three ways of calling .find() are equivalent:

$c:=New collection(7; 13; 27)
$result:=_.find($c; Formula($1.value>10))
$result:=_.find($c; Formula($1.result:=$1.value>10))
$result:=_.find($c; Formula(New object("result"; $1.value>10)))
// $result = 13

.findAll()

.findAll(collection : Collection; predicate : Callable { ; …param : any } ) : Collection
.findAll(collection : Collection; startFrom : Integer ; predicate : Callable { ; …param : any } ) : Collection

This is the same as .find(), but returns all of the matching items in collection as a new collection.


.findIndex()

.findIndex(collection : Collection; predicate : Callable { ; …param : any } ) : Integer
.findIndex(collection : Collection; startFrom : Integer ; predicate : Callable { ; …param : any } ) : Integer

This is the same as Collection.findIndex()open in new window, but allows you to use a Callable instead of a global method in v19 R5, and also provides a more flexible syntax for predicate.

NOTE

Time and Picture values cannot be searched for.

predicate receives the same parameters that a method/function called via Collection.findIndex()open in new window would. The predicate may:

  • Return a boolean
  • Set $1.result or $1.stop
  • Return a new object with .result and/or .stop

WARNING

If you are setting $1.result/stop directly, avoid nesting the call to this function in another Callable, but rather use an intermediate variable for the result of this function.

Example

These three ways of calling .findIndex() are equivalent:

$c:=New collection(7; 13; 27)
$result:=_.findIndex($c; Formula($1.value>10))
$result:=_.findIndex($c; Formula($1.result:=$1.value>10))
$result:=_.findIndex($c; Formula(New object("result"; $1.value>10)))
// $result = 1

.findIndices()

.findIndices(collection : Collection; predicate : Callable { ; …param : any } ) : Collection
.findIndices(collection : Collection; startFrom : Integer ; predicate : Callable { ; …param : any } ) : Collection

This is the same as findIndex(), but returns all of the matching indices. If no matches are found, an empty collection is returned.

TIP

.findIndexes() is an alias for this function.


.findLastIndex()

.findLastIndex(collection : Collection; predicate : Callable { ; …param : any } ) : Integer
.findLastIndex(collection : Collection; startFrom : Integer ; predicate : Callable { ; …param : any } ) : Integer

This is the same as _.findIndex(), but searches from the end of the collection (or startFrom) to the beginning.


.flatten()

.flatten(collection : Collection { ; recursive : Boolean }) : Collection

Returns a new non-shared collection with the flattened contents of collection. “Flattening” means that embedded collections are replaced inline with their items.

If recursive is passed and is true, embedded collections are themselves flattened recursively before being replaced.

Example

$embedded1:=New collection("foo"; "bar")
$embedded2:=New collection("three"; "four"; $embedded1)
$c:=New collection(1; 2; $embedded2; 5)

$flattened:=_.flatten($c)
// $flattened = [1, 2, "three", "four", ["foo", "bar"], 5]

$flattened:=_.flatten($c; True)
// $flattened = [1, 2, "three", "four", "foo", "bar", 5]

.flattenDeep()

.flattenDeep(collection : Collection) : Collection

A convenience function that calls flatten(collection; True).


.indexOf()

.indexOf(collection: Collection; toSearch : expression { ; startFrom : Integer { ; strict : Boolean }}) : Integer

This is the same as calling collection.indexOf()open in new window, but allows you to specify case/diacritical matching of strings in the strict parameter.


.indices()

.indices(collection : Collection; queryString : Text { ; …value : any { ; strict : Boolean }}) : Collection

This is the same as calling collection.indices()open in new window, but allows you to specify case/diacritical matching of strings in the strict parameter.

TIP

.indexes() is an alias for this function.


.intersection()

.intersection(left : Collection; right : Collection; strict : Boolean { ; propertyPath : Text }) : Collection

Calculates the intersection of left and right and returns the result as a new, non-shared collection. The result is a shallow copy of the source items, which will be from left if right is the same length or longer, or from right otherwise.

If strict is true, the collection item comparisons are case/diacritical-sensitive.

If the values in the collections are objects, then propertyPath may be passed to specify a path to the values to compare.

Example

$left:=New collection("foo"; "bar"; "baz")
$right:=New collection("bar"; "baz"; "qux")
$result:=_.intersection($left; $right)
// $result = ["bar", "baz"]

.lastIndexOf()

.lastIndexOf(collection: Collection; toSearch : expression { ; startFrom : Integer { ; strict : Boolean }}) : Integer

This is the same as _.indexOf(), but searches from the end of the collection (or startFrom) to the beginning.


.map()

.map(collection : Collection; func : Callable { ; …params : any } ) : Collection

This is similar to Collection.map()open in new window, but adds some extra functionality and allows you to use a formula in 4D v19 R5.

func receives an object ($1) with the following properties:

PropertyTypeDescription
.valueanyThe current item in the collection
.indexIntegerThe index of the current item in the collection
.collectionCollectionThe collection being mapped

Any params passed to .map() are passed to func as parameters after $1.

func may:

  • Return a value
  • Set $1.result or $1.stop

WARNING

If you are setting $1.result/stop directly, avoid nesting the call to this function in another Callable, but rather use an intermediate variable for the result of this function.


.reduce()

.reduce(collection : Collection; func: Callable) : any
.reduce(collection : Collection; func: Callable ; initValue : any { ; …param : expression }) : any

This is the same as Collection.reduce()open in new window, but allows you to use a Callable instead of a global method in v19 R5, and also provides a more flexible syntax for func.

func receives these parameters:

  • $1.value – The current element value to be processed
  • $2… – Any extra parameters passed to .reduce()

func may:

  • Set $1.accumulator or $1.stop
  • Return a new object with .accumulator and/or .stop

WARNING

If you are setting $1.result/stop directly, avoid nesting the call to this function in another Callable, but rather use an intermediate variable for the result of this function.


.some()

.some(collection : Collection; predicate : Callable { ; …param : any }) : Boolean
.some(collection : Collection; startFrom : Integer ; predicate : Callable { ; …param : any }) : Boolean

This is the same as Collection.some()open in new window, but allows you to use a Callable instead of a global method in v19 R5, and also provides a more flexible syntax for predicate.

predicate receives the same parameters that a method/function called via Collection.some()open in new window would. The predicate may:

  • Return a boolean
  • Set $1.result or $1.stop
  • Return a new object with .result and/or .stop

WARNING

If you are setting $1.result/stop directly, avoid nesting the call to this function in another Callable, but rather use an intermediate variable for the result of this function.

Example

These three ways of calling .some() are equivalent:

$c:=New collection(7; 13; 27)
$result:=_.some($c; Formula($1.value<10))
$result:=_.some($c; Formula($1.result:=$1.value<10))
$result:=_.some($c; Formula(New object("result"; $1.value<10)))
// $result = True

.sort()

.sort({ propertyPath : Text ; } collection : Collection { ; …collectionN : Collection }{ ; direction : Text }) : Collection
.sort({ propertyPath : Text ; } collection : Collection { ; …collectionN : Collection }{ ; func: Callable }) : Collection

This function sorts (using a stable merge sortopen in new window) one or more collections in parallel, using the same sort order for each collection. The sort order is determined by collection, which is returned.

NOTE

All of the collections passed to .sort() must be of the same length, and all of the values in each collection must be of the same type. The collections are sorted in place.

If propertyPath is passed, then the values in collection are assumed to be objects, and the values at the specified path are used for sorting.

If direction/func are not passed, the collections are sorted in ascending order. If direction is passed, it must be either "<" or ">", and the collections are sorted in ascending or descending order respectively.

NOTE

If direction/func are not passed, all of the values in each collection must be comparable using the <= or >= operator.

If func is passed, it must be a Callable that receives the values to be compared in $1 and $2, and returns a boolean.

The following errors are thrown:

ErrorCause
TypeErrorcollection was not passed or is not a collection
TypeErrorThe first parameter is neither a property path nor a collection
RangeErrordirection is not "<" or ">"
TypeErrorThe last parameter is an invalid type
TypeErrorOne of the collection parameters is not a collection
RangeErrorThe collections are not all the same length

Examples

$c1:=New collection(1; 3; 2)
$c2:=New collection("a"; "c"; "b")
_.sort($c1; $c2)
// $c1 = [1, 2, 3]
// $c2 = ["a", "b", "c"]

_.sort($c1; $c2; ">")
// $c1 = [3, 2, 1]
// $c2 = ["c", "b", "a"]

$c1:=_.json("[{ 'name': 'foo' }, { 'name': 'bar' }]")
$c2:=New collection(31; 27)
_.sort("name"; $c1; $c2)
// $c1 = [{name:"bar"}, {name:"foo"}]
// $c2 = [27, 31]

$c1:=New collection()
$c1.push(_.json("{ 'name': 'foo', 'info': { 'age': 31 }}"))
$c1.push(_.json("{ 'name': 'bar', 'info': { 'age': 27 }}"))
_.sort("info.age"; $c1; $c2)
// $c1 = [{name:"bar", info:{age:27}}, {name:"foo", info:{age:31}}]
// $c2 = [27, 31]

TIP

When using func, >= or <= will result in a stable sort, but > or < will not.


.splice()

.splice(collection : Collection; start : Integer { ; deleteCount : Integer { ; …items : any }}) : Collection

This function changes the contents of a collection by removing or replacing existing elements and/or adding new elements in place. The removed elements are returned as a new collection.

The start and deleteCount parameters are the same as in Collection.remove()open in new window. If items are passed, they are then inserted at start using a shallow copy.


.zip()

.zip(properties : Collection; values : Collection { ; options : Object }) : Object | Collection

Returns an object or collection with successive property/value pairs from the respective collections. What is returned depends on the options parameter:

OptionDefaultDescription
.output"object""object" or "collection"
.fillValueUndefinedThe value to use for any missing values in the other collection

If options.output is passed and is not "object" or "collection" (case-sensitive), then an error is thrown and the function returns null.

Object output

If options is not passed, options.output is not defined, or options.output is "object", then the result is an object whose keys come from properties and whose values come from values.

If options.fillValue is not defined and the two collections are not the same length, an error is thrown and null is returned.

If options.fillValue is defined and properties is shorter than values, an error is thrown and null is returned.

If options.fillValue is defined and properties is longer than values, the missing values will be filled with options.fillValue.

Collection output

If options.output is "collection", then the result is a collection where each item is a collection consisting of a property and value.

If options.fillValue is not defined and the two collections are not the same length, an error is thrown and null is returned.

If options.fillValue is defined, any missing values in the other collection will be filled with options.fillValue.

The following errors are thrown:

ErrorCause
TypeErroroptions.output is an invalid value
RangeErrorproperties.length < values.length
RangeErrorproperties and values have unequal lengths

Examples

$zipped:=_.zip(New collection("one"; "two"); New collection(1; 2))
// $zipped = { "one": 1, "two": 2 }

$zipped:=_.zip(New collection("one"; "two"; "three"); New collection(1; 2))
// $zipped = Null, _.error() = True

$zipped:=_.zip(\
  New collection("one"; "two"; "three"); \
  New collection(1; 2); \
  New object("fillValue"; "foo"))
// $zipped = { "one": 1, "two": 2, "three": "foo" }

$zipped:=_.zip(\
  New collection("one"; "two"); \
  New collection(1; 2); \
  New object("output"; "collection"))
// $zipped = [["one", 1], ["two", 2]]

$zipped:=_.zip(\
  New collection("one"; "two"; "three"); \
  New collection(1; 2); \
  New object("output"; "collection"))
// $zipped = Null, _.error() = True

$zipped:=_.zip(\
  New collection("one"; "two"; "three"); \
  New collection(1; 2); \
  New object("output"; "collection"; "fillValue"; "foo"))
// $zipped = [["one", 1], ["two", 2], ["three", "foo"]]

$zipped:=_.zip(\
  New collection("one"; "two"); \
  New collection(1; 2; 3); \
  New object("output"; "collection"; "fillValue"; "foo"))
// $zipped = [["one", 1], ["two", 2], ["foo", 3]]
Last Updated:
Contributors: Aparajita