Simplify oh-repeater filter expression

I need to add 10 keywords to an oh-repeater filter.
As the capabilites for expressions have recently been expanded (e.g. arrow functions) I’d like to ask if I can make use of any of these new possibilities to make the expression smarter.

This sounds a bit wired to me, can you please give an example.
Most I need in my (our) widgets is 5 „filters“.

It is about a widget to show various alarms. I receive the alarms in items containing a key word like overtemp, fire, load_error. But not alle alarm key words should be displayed, just the critical ones. That’s why the filter

component: oh-repeater
config:
  filter: loop.rItem.state == "PRIMARY_ALARM"||loop.rItem.state == "OVERTEMP"||loop.rItem.state == "OVERPOWER"||...
  for: rItem
  fragment: true
  groupItem: gAlarms
  sourceType: itemsInGroup

Did not try it out, but something like

["PRIMARY_ALARM", "OVERTEMP", "OVERPOWER"].includes(loop.rItem.state)

might make it a bit shorter …

This results in true but does not return a filtered array. Or maybe I just dont understand how to continue from here.

Cutting this expression into peaces :

  filter: loop.rItem.state == "PRIMARY_ALARM"

should give the same result as

  filter: (loop.rItem.state).includes("PRIMARY_ALARM")

and the same as

  filter: ("PRIMARY_ALARM").includes(loop.rItem.state)

so

  filter: ["PRIMARY_ALARM", "OVERTEMP", "OVERPOWER"].includes(loop.rItem.state)

should work

ok. Then maybe the widget does not support includes. It returned an unfiltered array. Thanks for your answer.

I am just checking in my environment…

Indeed, it is not working like that. :woozy_face:
Sorry!

Indeed. Nice code…

I’m on my phone so I can’t easily get the link, but try the object syntax suggested at the bottom of the Creating Personal Widgets doc page.

Edit:
Here’s the link: Creating Personal Widgets | openHAB

And here’s an example of what I mean. As long the values that you want to filter out are either not keys in the object or are keys that have some falsy value, then the object syntax is about as compact as you’ll get.

uid: filter_demo
props:
  parameterGroups: []
  parameters: []
tags: []
component: oh-list-card
config:
slots:
  default:
    - component: oh-repeater
      config:
        for: test
        sourceType: array
        in:
          - title: A
            filter: Apple
          - title: B
            filter: Banana
          - title: C
            filter: Cucumber
        filter: ({'Apple':1,'Cucumber':1})[loop.test.filter]
        fragment: true
      slots:
        default:
          - component: oh-list-item
            config:
              title: =loop.test.title

image

1 Like

one should get same result using

filter: (["Apple", "Cucumber"]).includes(loop.test.filter)

sorry, to be honest I just checked the

loop.rItem.state == "PRIMARY_ALARM"||loop.rItem.state == "OVERTEMP"||loop.rItem.state == "OVERPOWER"||...

part, which (as pointed out by @hmerk too) should produce same result as

["PRIMARY_ALARM", "OVERTEMP", "OVERPOWER"].includes(loop.rItem.state)

just written in a bit more “compact” way.

Would you mind sharing a sample yaml that gives you problems or more details on what you are trying to achieve since (at least I) don’t full understand the problem. Thx!

I have checked it in my installation and only get an empty result… Something wrong with the array…

for some reason you need to add “()”, i.e. modified example

uid: filter_demo
props:
  parameterGroups: []
  parameters: []
tags: []
component: oh-list-card
config:
slots:
  default:
    - component: oh-repeater
      config:
        for: test
        filter: (["Apple", "Cucumber"]).includes(loop.test.filter)
        sourceType: array
        in:
          - title: A
            filter: Apple
          - title: B
            filter: Banana
          - title: C
            filter: Cucumber
        fragment: true
      slots:
        default:
          - component: oh-list-item
            config:
              title: =loop.test.title

similary in original example above dictionary is wrapped with ()

({'Apple':1,'Cucumber':1})

EDIT: also

filter: '["Apple","Banana"].includes(loop.test.filter)'

and

filter: (["Apple","Banana"].includes(loop.test.filter))

works. Probably related to how yaml parser converts stuff (I guess similar to what is explained here Using Ternary Operators in Widget Expressions)

Sorry, but that’s not what OP wanted.
You are both definfing an array as the source type, but the the source type is a number of several items where he wants to filter only those with a given state.

I have tried it like I described before and it is not working, no differnce if the array of states is surrounded by brackets.

I will do another test with the JSON format later on…

1 Like

As said above, I probably don’t see the whole picture, but imho

can be simplified via

component: oh-repeater
config:
  filter: (["PRIMARY_ALARM", "OVERTEMP", "OVERPOWER"].includes(loop.rItem.state))
  for: rItem
  fragment: true
  groupItem: gAlarms
  sourceType: itemsInGroup
2 Likes

Thanks to all of you for the support.
I can confirm that this expression works:

1 Like

May I ask again for assistance?
given this filter expression within oh-repeater as above:

filter: (["PRIMARY_ALARM", "OVERTEMP", "OVERPOWER"].includes(loop.rItem.state))

How would this expression look like outside oh-repeater where I want to get the total number of matches from items within a group? Something like:

text: =(["PRIMARY_ALARM", "OVERTEMP", "OVERPOWER"].includes(items.gAlarmItems.member)).size

I think I need to iterate through members to get their states, but now I am at the end of my capabilities

Not really feasible, alas. The items object does not give you full access to an item, only the state and display state. The only way to get the information is from an api call such as the repeater makes. There are some workarounds if you nest a repeater inside another repeater. The most straightforward looks like this:

- component: oh-repeater
  config:
    for: array1
    ...whatever repeater options you need, including fitlers
    map: loop.array1_source <-- Put the whole repeater results array into each element of the loop
  slots:
    default:
      - component: f7-block
        config:
          visible: =loop.array1_idx == 0 <-- This block is now a block that will be rendered only once but has the full repeater result in loop.array1
        slots:
          default:
            - component: Label
              config:
                text: =loop.array1.length <-- Any component in the block can now access the array1 methods and properties
            - component: oh-repeater
              config:
                for: array2
                sourceType: array
                in: =loop.array1 <-- The repeater doesn't need to make another API call, it can just use the result array from the first repeater
              slots:
                defaults:
                  ...use loop.array2 to do whatever you want with your repeated elements
1 Like

Thanks Justin,
a little bit beyond my capabilities this workaround but try to recap your solution.
Thanks a lot!