How to check that a value exists in a json list of key/value pairs

I know I could do this with a function node (or perhaps a jsonata value in a switch node), but I'm wondering if there's a (simple) way to do this with the basic (i.e. core) nodes that come with a fresh node red install...

I have a flow variable stored as JSON that contains a list of key/value pairs and I want to check if an incoming message's values are among the values in the flow variable.

E.g. the incoming message has values for keys names "list", "title", and "changed". I want to see if the msg.payload.list value matches one of the corresponding flow.spoken_reminders[x].list values in the flow variable. I then want to see if the msg.payload.title value matches one of the corresponding flow.spoken_reminders[x].reminder value. Then I just want to see if msg.payload.changed matches "duedate".

I wrote a working example that checks 3 static members of the flow variable. But I'd like it to work on a flow variable containing a list of variable size. It would also be nice if there was a single output (as opposed to the 3 outputs of each switch in my example. It would even be cooler if I could do all 3 checks (on list, title, and changed) in a single node. The ideal solution would do all that and also use the "Spoken Reminders" template without need to store it in a flow variable. But any solution which satisfies any one of these goals would be helpful.

Here's my static example. It initializes the flow variable. Then you can run the test and get a single matching output in the debug panel...

[{"id":"c6d2cb6e.6beec8","type":"template","z":"f1e2fb56.ef74a","name":"Spoken Reminders","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"[\n    {\n      \"list\": \"ToDo Home Recurring\",\n      \"reminder\": \"Recycling is every other Monday\"\n    },\n    {\n      \"list\": \"ToDo Home Recurring\",\n      \"reminder\": \"Trash pickup is Wednesday\"\n    },\n    {\n      \"list\": \"ToDo Home Recurring\",\n      \"reminder\": \"Compost pickup is Thursday\"\n    }\n]\n","output":"json","x":810,"y":200,"wires":[["3f2071af.fa19a6"]]},{"id":"39ecc5db.dafeaa","type":"switch","z":"f1e2fb56.ef74a","name":"list","property":"payload.list","propertyType":"msg","rules":[{"t":"eq","v":"spoken_reminders[0].list","vt":"flow"},{"t":"eq","v":"spoken_reminders[1].list","vt":"flow"},{"t":"eq","v":"spoken_reminders[2].list","vt":"flow"}],"checkall":"false","repair":false,"outputs":3,"x":950,"y":140,"wires":[["755f6d4c.c4ef64"],["755f6d4c.c4ef64"],["755f6d4c.c4ef64"]]},{"id":"a035cd29.01691","type":"inject","z":"f1e2fb56.ef74a","name":"Init","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":650,"y":200,"wires":[["c6d2cb6e.6beec8"]]},{"id":"82cc268f.867a6","type":"debug","z":"f1e2fb56.ef74a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1230,"y":200,"wires":[]},{"id":"3f2071af.fa19a6","type":"change","z":"f1e2fb56.ef74a","name":"Set Spoken Reminders","rules":[{"t":"set","p":"spoken_reminders","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1030,"y":200,"wires":[["82cc268f.867a6"]]},{"id":"3641807d.d648","type":"template","z":"f1e2fb56.ef74a","name":"Test false event","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"{\n\"id_new\": \"0BFCB5FC-14F8-43EB-AFAB-A37894E31D83\",\n\"when\": \"2021-06-20T18:52:36\",\n\"iscurrent\": \"1\",\n\"title\": \"Take my mid-day sulfasalazine\",\n\"id_old\": \"086A3F9B-308D-54CE-8CBD-2EBC24508025\",\n\"id_cur\": \"0BFCB5FC-14F8-43EB-AFAB-A37894E31D83\",\n\"val_new\": \"2021-06-20T18:52:36\",\n\"val_old\": \"\",\n\"val_cur\": \"2021-06-20T18:52:36\",\n\"list\": \"Reminders Recurring\",\n\"changed\": \"completeddate\"\n}","output":"json","x":800,"y":160,"wires":[["39ecc5db.dafeaa"]]},{"id":"28182ab.14f24d6","type":"inject","z":"f1e2fb56.ef74a","name":"Test","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":650,"y":140,"wires":[["3641807d.d648","53877301.bd7904"]]},{"id":"6313100d.cb17e8","type":"debug","z":"f1e2fb56.ef74a","name":"Test","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1310,"y":140,"wires":[]},{"id":"53877301.bd7904","type":"template","z":"f1e2fb56.ef74a","name":"Test true event","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"{\n\"id_new\": \"0BFCB5FC-14F8-43EB-AFAB-A37894E31D83\",\n\"when\": \"2021-06-20T18:52:36\",\n\"iscurrent\": \"1\",\n\"title\": \"Recycling is every other Monday\",\n\"id_old\": \"086A3F9B-308D-54CE-8CBD-2EBC24508025\",\n\"id_cur\": \"0BFCB5FC-14F8-43EB-AFAB-A37894E31D83\",\n\"val_new\": \"2021-06-20T18:52:36\",\n\"val_old\": \"\",\n\"val_cur\": \"2021-06-20T18:52:36\",\n\"list\": \"ToDo Home Recurring\",\n\"changed\": \"duedate\"\n}","output":"json","x":800,"y":120,"wires":[["39ecc5db.dafeaa"]]},{"id":"755f6d4c.c4ef64","type":"switch","z":"f1e2fb56.ef74a","name":"rem","property":"payload.reminder","propertyType":"msg","rules":[{"t":"eq","v":"spoken_reminders[0].title","vt":"flow"},{"t":"eq","v":"spoken_reminders[1].title","vt":"flow"},{"t":"eq","v":"spoken_reminders[2].title","vt":"flow"}],"checkall":"false","repair":false,"outputs":3,"x":1070,"y":140,"wires":[["b83b5cb0.695738"],["b83b5cb0.695738"],["b83b5cb0.695738"]]},{"id":"b83b5cb0.695738","type":"switch","z":"f1e2fb56.ef74a","name":"due","property":"payload.changed","propertyType":"msg","rules":[{"t":"eq","v":"duedate","vt":"str"}],"checkall":"false","repair":false,"outputs":1,"x":1190,"y":140,"wires":[["6313100d.cb17e8"]]}]

Not 100% sure i understand your goal but this is my interpretation - if it is no good, then hopefully it will give you some clues?

[{"id":"9662df3ac6ec9b64","type":"function","z":"bec69dbd.8d622","name":"find event","func":"\nconst reminders = msg.reminders;\nconst event = msg.payload;\n\n//find one that matches\nconst item = reminders.find(e => {\n    return e.list ==event.list && e.reminder == event.title\n})\n\n//if found and event.changed is \"duedate\"...\nif (item && event.changed == \"duedate\") {\n    return msg; //return the msg, this payload is a match\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1960,"y":780,"wires":[["e80a647e808ffe67"]]},{"id":"c4cb876e5ce23ea4","type":"inject","z":"bec69dbd.8d622","name":"Test","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1430,"y":760,"wires":[["ef364365912ac60a"]]},{"id":"ef364365912ac60a","type":"template","z":"bec69dbd.8d622","name":"Test true event","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"{\n\"id_new\": \"0BFCB5FC-14F8-43EB-AFAB-A37894E31D83\",\n\"when\": \"2021-06-20T18:52:36\",\n\"iscurrent\": \"1\",\n\"title\": \"Recycling is every other Monday\",\n\"id_old\": \"086A3F9B-308D-54CE-8CBD-2EBC24508025\",\n\"id_cur\": \"0BFCB5FC-14F8-43EB-AFAB-A37894E31D83\",\n\"val_new\": \"2021-06-20T18:52:36\",\n\"val_old\": \"\",\n\"val_cur\": \"2021-06-20T18:52:36\",\n\"list\": \"ToDo Home Recurring\",\n\"changed\": \"duedate\"\n}","output":"json","x":1580,"y":760,"wires":[["778982f7002ce2f6"]]},{"id":"560191aec30543e8","type":"inject","z":"bec69dbd.8d622","name":"Test","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1430,"y":800,"wires":[["08d75fb9390be08f"]]},{"id":"08d75fb9390be08f","type":"template","z":"bec69dbd.8d622","name":"Test false event","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"{\n\"id_new\": \"0BFCB5FC-14F8-43EB-AFAB-A37894E31D83\",\n\"when\": \"2021-06-20T18:52:36\",\n\"iscurrent\": \"1\",\n\"title\": \"Take my mid-day sulfasalazine\",\n\"id_old\": \"086A3F9B-308D-54CE-8CBD-2EBC24508025\",\n\"id_cur\": \"0BFCB5FC-14F8-43EB-AFAB-A37894E31D83\",\n\"val_new\": \"2021-06-20T18:52:36\",\n\"val_old\": \"\",\n\"val_cur\": \"2021-06-20T18:52:36\",\n\"list\": \"Reminders Recurring\",\n\"changed\": \"completeddate\"\n}","output":"json","x":1580,"y":800,"wires":[["778982f7002ce2f6"]]},{"id":"e80a647e808ffe67","type":"debug","z":"bec69dbd.8d622","name":"Test","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":2110,"y":780,"wires":[]},{"id":"778982f7002ce2f6","type":"template","z":"bec69dbd.8d622","name":"reminders","field":"reminders","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n    {\n        \"list\": \"ToDo Home Recurring\",\n        \"reminder\": \"Recycling is every other Monday\"\n    },\n    {\n        \"list\": \"ToDo Home Recurring\",\n        \"reminder\": \"Trash pickup is Wednesday\"\n    },\n    {\n        \"list\": \"ToDo Home Recurring\",\n        \"reminder\": \"Compost pickup is Thursday\"\n    }\n]","output":"json","x":1800,"y":780,"wires":[["9662df3ac6ec9b64"]]}]

NOTES:

  • This will only find 1 match (if you want multiple outputs from 1 output for every match we'll need to change the mechanics a bit)
  • This will work regardless of now many elements in the template data
  • The tests are case sensitive (i.e. "ToDo Home Recurring" will not match "todo home recurring")

Totally cool. Yes, this is essentially what I want. I was wondering how to "add" the "Spoken Reminders" template for the check without blowing away the payload. It hadn't occurred to me that I could create a new property in a template node. So simple. It's a "Doh!" moment, for sure.

Out of curiosity, is there no way to do either the list:list or title:reminder check in a switch node? I.e. does contains in the switch node work on a list in a json object and can it check a value of a specific key, like this pseudocode hypothetical switch entry:

msg.reminders[0..end].list contains msg.payload.list

Or does contains only do substring matches?

contain is for substring matches yes.

Possibly in JSONata but it took me 3 mins to knock up that example. JSONata would proably take me about an hour to figure out - then I'd forget how it works in about 2 days - I tend to stick to JS as it is much more prolific.

I have the same issue with my jsonata switch checks. I forget what they do too. I started naming such switch nodes to include a description of each output, e.g. "value 1. meets condition 1 2. meets condition 2...". It would be nice if in the jsonata you could set an output label, but yeah - I'm avoiding jsonata too for the same reason.

And BTW, if you're curious about my goal, I'm in the middle of upgrading my "Spoken Reminders" stuff. It's just for fun, really, but also useful. A short description:

  1. I have a WeMo motion sensor in the kitchen. When it senses motion, it sends a signal to my WeMo node which outputs the event
  2. I have a script that takes a rules file that checks if there are any pending spoken reminders that are active certain hours of certain days. If there are, it speaks the reminder through my stereo. (It also limits reminders to once in a 3 hour period, but is limited to weekly recurrences only - it can't do say, "every other week", which is the case for my recycling reminder)
  3. I recently augmented that script to also check a file that contains the next due date of a reminder from my iOS reminders app. So if I have completed (e.g.) "Take out the trash" or if "Take out the recycling" is the "off" week, the spoken reminder will not issue.
  4. I then wrote a script to synch both incomplete iOS reminders and reminders that have been modified since the last check. It processes the modified reminders to create the change events (of which you see an example in the flow I pasted as "Test frue/false event").
  5. Then I implemented a flow that runs fswatch (installed via homebrew) that watches the Reminders Database on my Mac Mini. It spits out a message whenever the database is modified, from which I pick out the change events and send it through the filter I asked about above.

Really complicated, I know - but fun to see work! I plan to use it for other automations too.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.