KNOWLEDGE BASE
Log In    |    Knowledge Base    |    4D Home
Tech Tip: Deep Flat Nested Collection
PRODUCT: 4D | VERSION: 17 | PLATFORM: Mac & Win
Published On: August 2, 2019

The documentation of collection.reduce( ) showcases the power of reduce() using an example of flatting collection with nested collection as elements. However, this example and the project method it uses, Flatten, only deals with first-level nested collections. Ideally, a flat method should provide the option to deep flat to the lowest level.

Below is a deep flat method that can be used to flat nested collections to the lowest level. It returns a new collection with flattened collection. In addition, it takes optional parameters allowing developer to specify the accumulator and the desired level the method goes to.

// Name: Collection_DeepFlat
// Description: Method will return a collection in which a part of collection is copied
// to another location of the same acollection without changing its size
// Parameters:
// $1 (Object) - Object containing two properties:
// $1.value: each element passed from reduce()
// $1.accumulator: value to be modified by the function
// $2 (Longint)- Zero based index to specify how many levels the flatten method goes to
//
// Return
// $0(Collection)- a flattened collection

C_OBJECT($1)
C_LONGINT($2)
C_LONGINT($level)
$level:=MAXLONG
If (Not(Undefined($2)))
  $level:=$2
End if
If ($1.accumulator=Null)
  $1.accumulator:=New collection
End if
If ((Value type($1.value)=42) & ($level>0))
  $level:=$level-1
  $1.accumulator.combine($1.value.reduce("Collection_DeepFlat";\
      New collection;$level))
Else
  If (Value type($1.value)=42)
    $1.accumulator.combine($1.value)
  Else
    $1.accumulator.combine(New collection($1.value))
  End if
End if



Below is an example of using Collection_DeepFlat in action:

C_COLLECTION($c)
$c:=New collection(1;2;3)
C_COLLECTION($col;$result)
$col:=New collection(New collection($c;$c);New collection($c;$c;$c);New collection($c;$c;New collection($c;$c));$c)
$temp:=$col
//$col: [[[1,2,3],[1,2,3]],[[1,2,3],[1,2,3],[1,2,3]],[[1,2,3],[1,2,3],[[1,2,3],[1,2,3]]],[1,2,3]]
$result:=$col.reduce("Collection_DeepFlat")
//All level Flatten $col: [1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3]
$result:=$col.reduce("Collection_DeepFlat";New collection;0)
//One level flattern $col: [[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[[1,2,3],[1,2,3]],1,2,3]
$result:=$col.reduce("Collection_DeepFlat";New collection;1)
//Two levels flatten $col: [1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,[1,2,3],[1,2,3],1,2,3]

Commented by John Carr on November 22, 2023 at 6:20 PM
Character limit... :( var $1; $vJ_Object : Object var $2; $vB_FlattenObjects : Boolean $vJ_Object:=$1 If (Count parameters>-2) $vB_FlattenObjects:=$2 End if If ($vJ_Object.accumulator=Null) $vJ_Object.accumulator:=New collection End if
Commented by John Carr on November 22, 2023 at 6:19 PM
This won't flatten any collections within any objects within any of the collection levels. You would need to check for objects and then loop through all its properties and then recursively call .combine().reduce("Collection_DeepFlat") again for collection or object values from the original object. eg. (note: this doesn't use levels but could be refactored accordingly if required). var $1; $vJ_Object : Object var $2; $vB_FlattenObjects : Boolean $vJ_Object:=$1 If (Count parameters>-2) $vB_FlattenObjects:=$2 End if If ($vJ_Object.accumulator=Null) $vJ_Object.accumulator:=New collection End if Case of : (Value type($vJ_Object.value)=Is collection) $vJ_Object.accumulator.combine($vJ_Object.value.reduce(Current method name; New collection; $vB_FlattenObjects)) : ((Value type($vJ_Object.value)=Is object)