Expand support for RowLookup

Description of the limitation and why it is relevant to address

As a developer I want to be able to show a value (OutputField) in a row of a DynamicArray based on another value in the same row.

Currently, RowLookupis only supprted (and evaluated) in the context of min, max and visible(although I’m not really sure about the last one!).

An OutputField accepts RowLookup directly as a value argument, but not as an argument to FunctionLookup.

array = DynamicArray('My array')
array.param_x = NumberField('X')
array.param_y = OutputField('Y', value=RowLookup(param_x))  # This is possible
array.param_z = OutputFiled('Z', value=FunctionLookup(add, RowLookup('param_x'), 2)  # This is not possible as RowLookup() does not get evaluated before being passed to the add function

I think this is relevant for the VIKTOR platform because this functionality already exist for normal Lookup. RowLookupis the equivalent of Lookup in the context of a DynamicArray .

Given the many advantages of DynamicaArrayit’s only logical to have the same functionalities for RowLookup as well.

Submitter proposed design (optional)

One option is forRowLookup to be accepted and evaluated as an argument to FunctionLookupso that OutputFiled can have its value dynamically set based on values in the same row of a DynamicArray just like the example above.

Another option is always accepting “$row” in Lookup just like this example from the documentation:

_show_y = DynamicArrayConstraint('array_name', IsTrue(Lookup('$row.param_x')))

Last option (easy to use, but more work to implement) is to pass the row index as kwargs to the Callable. For example:

def custom_log_function(params, row_index, **kwargs):
    return np.log(params.array[row_index].param_x)

array = DynamicArray('My array')
array.param_x = NumberField('X')
array.param_y = OutputField('Y', value=custom_log_function)

Current workarounds

As far as I know, there are no workarounds now, but if there is one, I would love to hear that!

Hi @Sina ,

You can use callback functions here, that returns a list of values per row.


def get_output_values(params, **kwargs):
    return [row.value for row in params.dyn]

class Parametrization(ViktorParametrization):
    dyn = vkt.DynamicArray("Test")
    dyn.value = vkt.NumberField("Test")
    dyn.output = vkt.OutputField("Same output", value=get_output_values)

It will automatically map the output values to the DynamicArray rows.

2 Likes

@rvandijk you’re too quick. Here is my AppBuilder example:

import viktor as vkt
import numpy as np


def custom_log_function(params, **kwargs):
    # Iterate over the DynamicArray rows and calculate each row
    logs = []
    for row in params.array:
        x = row.get("param_x", 0)
        if not x:
            logs.append(None)
        else:
            logs.append(np.log(x))
    
    return logs


class Parametrization(vkt.Parametrization):
    array= vkt.DynamicArray("My array", default=[{"param_x": 0}])
    array.param_x = vkt.NumberField("Number 1", default=0)
    array.log_output = vkt.OutputField("Log", value=custom_log_function)


class Controller(vkt.Controller):
    parametrization = Parametrization
2 Likes

Wow!

I did not know that it automatically maps the values! :exploding_head:

Thanks @rvandijk and @mslootweg

2 Likes