Skip to content

Expressions: List and object support #1179

@olemartinorg

Description

@olemartinorg

Right now, the expression language does not work with lists/arrays or objects, and will simply return null if you try to look one up from the data model. This is unfortunate, and the language should support these values - but we need to provide tools to manipulate them as well.

Needed functionality:

  • list function
    • Right now, all expressions are JSON arrays. If we want to support lists/arrays in the expression language, we need a way to represent a static list of values. Say, for example, that you wanted to check if a data model path is in a list of values with ["in", ["dataModel", ...], ["value1", "value2", "value3"]], the expression engine would think ["value1", "value2", "value3"] is another expression - and halt when the function value1 is not found.
    • So, to mitigate this problem, we could create a list function that simply takes any amount of parameters and returns them as a list/array. So the above could be written as ["in", ["dataModel", ...], ["list", "value1", "value2", "value3"]].
  • listLength function (or similar - decide in tandem with the string length function name in Expressions: New simple functions #1176)
    • An obvious function to include is one to measure the length of a list (given as the first argument).
  • Lookup a group and list binding
    • A group binding binds to a list/array of objects. If we support both data types in the expression language, we should support looking up these values using the component function. I think the most obvious thing here is to extend component to work on any binding type (if there is only one binding), and allow you to specify which binding (such as address) as a second parameter if the component has multiple bindings.
  • objKeys function (or similar)
    • A function that returns a list of keys inside an object, simply Object.keys()
  • pluck function
    • This function should be the powerhouse of the object support. It should pluck a value from an object (given a key). If given a list as a parameter, it should pluck out the key from every object in the list and return a list.
    • Example: ["pluck", "name", [{"name": "Ole Martin"}, {"name": "Per"}]] => ["Ole Martin", "Per"]
    • We should create robust tests for this so we don't end up supporting side-effects like looking up the length of a list of lists (because list.length is valid in javascript)
  • atIndex function
    • This function is for lists what pluck is for objects. It is named differently to indicate they are not interchangeable, but they operate the same. If you ["atIndex", 1, ["list", 1, 2, 3]] you should get 2 back.
    • We should create robust tests for this so we don't end up supporting side-effects like picking characters from a string.
  • first function
    • First conceived in Dynamiske uttrykk: Mulighet til å vise tekst fra skjult felt #1669 (comment)
    • Similar to atIndex, but in cases where you don't know the index. This way you can use an expression to find the first element in a list that matches another expression.
    • If this uses the value function for sub-expressions, it would ruin cases where you would like to use both the value from the current context (such as in optionFilter) and this sub-expression to evaluate each list item. For that reason we might want to use something else, such as an item function that behaves much like value.

Other simple functions (moved from #1176):

  • sum
    • Should take any amount of numbers as input, and also lists of numbers. All these numbers should be summed, and the resulting number should be returned. null and non-numeric values should be considered to be 0 (i.e. no effect on the sum).
  • average
    • Same as above, but should produce the average of all the numbers summed. null and non-numeric values should be considered entries when calculating the average, as we should make a filtering function for those cases where invalid values should not be considered instead.
  • in
    • Shortcut function similar to equals, but compares the first argument against multiple others to check if the first argument matches either one of them. Should support lists of items as parameters, and should then check inside the lists as well.
  • notIn
    • Synonym for ["not", ["in", ....]]

Other functions we should consider:

  • map (see argv in Expressions: Allow for re-use of expressions altinn-studio#16764)
    • As an example, you could ["map", ["upperCase", ["argv", 0]], ["list", "foo", "bar"]] to get ["FOO", "BAR"]
    • This would require deeper expression engine support than what is implemented for functions right now, as this is similar to a closure/callback data type.
  • filter
  • reduce
  • sort (for lists of strings/numbers)
  • reverse (for lists of strings/numbers)
  • concat support for joining lists?

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/logicrelated to logic/dynamics/expressionskind/feature-requestNew feature or requestorg/ssbIssues relevant for Statistisk sentralbyrå.squad/dataIssues that belongs to the named squad.status/for-considerationIssues that will be considered for future deliveries

    Type

    No type

    Projects

    Status

    In Progress

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions