# Interactive Parameterized Dictionary

**URL:** <https://discourse.holoviz.org/t/interactive-parameterized-dictionary/5735>\
**Category:** Param\
**Tags:** param, panel\
**Created:** [July 18, 2023, 1:53pm UTC](https://discourse.holoviz.org/t/interactive-parameterized-dictionary/5735 "2023-07-18T13:53:13Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![GuyattDL](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/guyattdl/32/5143_2.png) [@GuyattDL](https://discourse.holoviz.org/u/GuyattDL)\
**Post date:** [July 18, 2023, 1:53pm UTC](https://discourse.holoviz.org/t/interactive-parameterized-dictionary/5735/1 "2023-07-18T13:53:13Z")

</div>

Hi all,

I am building a Panel app and I have been using Param to create a parameterized DataHolder class.

One of the features of this class I would like is for users to be able to define group\_name : members\_list which will be used to filter a param.DataFrame attribute of my class.

The members list is made up of a selection from the unique values on one of the dataframe columns and the group\_name is user defined with text input.

I have read lots of the conversations on this topic via [#598](https://github.com/holoviz/param/pull/598) but I still can’t decipher what is the correct way to write an updater function such that users can define and edit the available options for groups.

---

<div class="post-metadata">

**Author:** ![GuyattDL](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/guyattdl/32/5143_2.png) [@GuyattDL](https://discourse.holoviz.org/u/GuyattDL)\
**Post date:** [July 18, 2023, 2:01pm UTC](https://discourse.holoviz.org/t/interactive-parameterized-dictionary/5735/2 "2023-07-18T14:01:16Z")

</div>

```auto
class Data(param.Parameterized):

  df = param.DataFrame(precedence=-1)
  groups = param.Dict(default={}) # this doesn't seem to have the parameterized functionality I expect
# Perhaps something like below instead?
# groups = param.Selector(default = {}, check_on_set=False, instantiate=True)
  group_members = param.ListSelector(default=[])
  group_name = param.String(default="")
  
  add_group_button = param.Action(
      lambda x: x.param.trigger("add_group_button"), label="Create Group"
  )

  @param.depends("add_group_button", watch=True)
  def add_selection_to_group(
      self, group_name: str = None, group_members: list = None
  ):
      if group_name is not None and group_members is not None:
          self.groups[group_name] = group_members
      else:
          self.groups[self.group_name] = self.group_members

      self.param.trigger("groups")
      self.group_members = []
      self.group_name = ""

```

This is my work in progress but It’s not really allowing me to access the keys and values to populate widgets for User editing.

Please let me know if anything is unclear.

Any help is greatly appreciated.

DG

---

<div class="post-metadata">

**Author:** ![johann](https://discourse.holoviz.org/letter_avatar_proxy/v4/letter/j/54ee81/32.png) [@johann](https://discourse.holoviz.org/u/johann)\
**Post date:** [July 29, 2023, 6:59pm UTC](https://discourse.holoviz.org/t/interactive-parameterized-dictionary/5735/3 "2023-07-29T18:59:24Z")

</div>

Hi  
Take a Look at the region/country example at  
[https://panel.holoviz.org/how\_to/param/dependencies.html](https://panel.holoviz.org/how_to/param/dependencies.html)

Its approach should work for yours as well.  
I personally would go with a different approach though, wrapping the group name and the list in a param class and add a list of these objects to your class via classselector.  
May sketch something up when I’m back in front of a computer,but may take a few days.  
Johann

---

<div class="post-metadata">

**Author:** ![johann](https://discourse.holoviz.org/letter_avatar_proxy/v4/letter/j/54ee81/32.png) [@johann](https://discourse.holoviz.org/u/johann)\
**Post date:** [July 30, 2023, 10:31am UTC](https://discourse.holoviz.org/t/interactive-parameterized-dictionary/5735/4 "2023-07-30T10:31:05Z")

</div>

DG,  
It’s raining, … so thought about this a little more.

On a high level you seem to want the user to be able to do 2 separate things via widgets on a UI:

1. create Groups (having a group\_name and a list of column values to filter for)
2. select one of the Group filters defined and apply it to the DF

I tweaked your example along these lines so you can play a little bit more.  
The main challenge i think you have is that although the param.Selector accepts a dict for the list of objects it does so only at init (internally it then populates .names and .objects attributes). So not easy to update _later_  
Hence I splitted your self.groups into an internal self.\_groups (that holds the actual group\_name/members) and a self.use\_group that allows the user to select a group.

```auto
import param
import panel as pn
pn.extension()

class Data(param.Parameterized):

    # dataframe and user-defined filters (empty at start)
    df = param.DataFrame(precedence=-1)
    _groups = param.Dict(precedence=-1, default={}, doc='Holds available Groups details') 

    # parameters and buttons to allow user to create filter groups
    group_name = param.String()
    group_members = param.ListSelector(
                        default=[], # empty by default, letting user to select some
                        objects=['A', 'B', 'C', 'D']) # unique values of the DF column
    create_group = param.Action(lambda self: self._cb_create_group())

    # parameter to allow user to select one of the existing Groups and apply
    use_group = param.Selector(default=None, doc='Group to apply')

    def _cb_create_group(self):
        ''' Will set a new group or overwrite an existing one. 
            It will also trigger a filtering of the data with the new/updated filter 
        '''
        if not self.group_name or not self.group_members:
            print('Specify group_name and group_members')
            return
        if self.group_name in self._groups:
            print(f'group_name "{self.group_name}" already exists. Ignored')
            return

        print(f'Create group: {self.group_name}, {self.group_members}')
        # update currently defined lists with the user input
        self._groups[self.group_name] = self.group_members

        # update the use_groups available list of groups and select the newly added
        # one (which will trigger a rerun of the DF filtering)
        # doing it in the sequence below will ensure the widget gets updated as well
        # (doing value first and then objects will update the actual use_group parameter
        # but not the widget :-()
        self.param.use_group.objects = list(self._groups.keys())
        self.use_group = self.group_name
        print(f'updated use_group parameter/widget: {self.use_group}, {self.param.use_group.objects}')

        
    @param.depends('use_group', watch=True)
    def filter_df(self):
        ''' filters DF based on filter selected in groups '''

        print(f'Filter with: {self.use_group}: {self._groups[self.use_group]}')
       
    def view(self):
        ''' Show widgets to allow user to update filters and select a filter '''
        return pn.Row(
                    pn.Param(self, parameters=['group_name', 'group_members', 'create_group'],
                             name='Create Filter Group'), 
                    pn.widgets.Select.from_param(self.param.use_group))

data_ui = Data()
data_ui.view()

```
