Store and check for changes in JSON

Hello,

Brand new to Node-RED, not quite as new to programming and stuff. (not used JSONata before) I'm trying to create a flow that monitors changes in a JSON array. I fetch on an interval a JSON array of object, each containing an ID and a quantity property. I need to figure out when any quantity has changed, and invoke a node for each item changed.

I figured out I can use the flow object to store data. So then I want to compare the payload quantities with those of the stored data. What's a good way to accomplish this?

In plain Javascript I'm looking to do something like this:

for (let item of msg.payload) {
	if (item.quantity !== flow.store.find(e => item.id).quantity) {
		invokeNextNode(item)
	}
}

flow.store = msg.payload

Hello Lombra,

Something like this ?

[{"id":"f5c75d5369b36f29","type":"inject","z":"5847b7aa62131d37","name":"items","props":[{"p":"payload.items","v":"[{\"id\":\"001\",\"qty\":11},{\"id\":\"002\",\"qty\":12},{\"id\":\"003\",\"qty\":13},{\"id\":\"004\",\"qty\":14},{\"id\":\"005\",\"qty\":15}]","vt":"json"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"str","x":350,"y":1460,"wires":[["87d3cd984e9d92b0"]]},{"id":"ba7534250f747f2a","type":"debug","z":"5847b7aa62131d37","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":730,"y":1500,"wires":[]},{"id":"79e82e6b9fd7a355","type":"inject","z":"5847b7aa62131d37","name":"changed items","props":[{"p":"payload.items","v":"[{\"id\":\"001\",\"qty\":12},{\"id\":\"002\",\"qty\":12},{\"id\":\"003\",\"qty\":13},{\"id\":\"004\",\"qty\":16},{\"id\":\"005\",\"qty\":15}]","vt":"json"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":320,"y":1540,"wires":[["87d3cd984e9d92b0"]]},{"id":"87d3cd984e9d92b0","type":"function","z":"5847b7aa62131d37","name":"","func":"let oldItems = flow.get(\"items\") || []\n\nlet newItems = msg.payload.items;\nflow.set(\"items\", newItems)\n\nlet changedItems = []\n\nif (oldItems.length > 0) {\n\n    newItems.forEach(newItem => {\n        if (oldItems.find(el => el.id === newItem.id).qty !== newItem.qty) {\n            changedItems.push(newItem);\n        }\n    \n})\n}\n\nmsg.payload = changedItems;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":1500,"wires":[["ba7534250f747f2a"]]},{"id":"ce95890a3e61aa66","type":"comment","z":"5847b7aa62131d37","name":"changed items","info":"","x":750,"y":1440,"wires":[]}]

[EDIT]
you may need a couple of more if statements to check whether there is a match after the .find
because i made the assumption that the items array always has the same items to compare

I think that should work, but would it be possible using expression switches instead? I wanted to avoid functions, if at all possible.

The criteria is actually rather that quantity becomes > 0 when previously it was 0. (or the given item did not exist)

I've tried doing something like this, but it's not really working. I think the expression in the second switch is not doing what I expect.

[{"id":"f5c75d5369b36f29","type":"inject","z":"f6f2187d.f17ca8","name":"items","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"id\":1,\"qty\":0},{\"id\":2,\"qty\":2},{\"id\":3,\"qty\":1},{\"id\":4,\"qty\":0},{\"id\":5,\"qty\":5}]","payloadType":"json","x":750,"y":400,"wires":[["6c498efff6b4d46b"]]},{"id":"79e82e6b9fd7a355","type":"inject","z":"f6f2187d.f17ca8","name":"changed items","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"id\":1,\"qty\":0},{\"id\":2,\"qty\":0},{\"id\":3,\"qty\":3},{\"id\":4,\"qty\":0},{\"id\":5,\"qty\":1}]","payloadType":"json","x":720,"y":480,"wires":[["6c498efff6b4d46b"]]},{"id":"8e9356ac6db955bb","type":"debug","z":"f6f2187d.f17ca8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1370,"y":400,"wires":[]},{"id":"d11023f40c7e9649","type":"switch","z":"f6f2187d.f17ca8","name":"","property":"payload.qty","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num"}],"checkall":"true","repair":true,"outputs":1,"x":1050,"y":440,"wires":[["adc9b1b299e23d00"]]},{"id":"6c498efff6b4d46b","type":"split","z":"f6f2187d.f17ca8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":910,"y":440,"wires":[["d11023f40c7e9649"]]},{"id":"9d49fd57a50d89da","type":"join","z":"f6f2187d.f17ca8","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":1350,"y":480,"wires":[["d4fee21f15966565"]]},{"id":"d4fee21f15966565","type":"change","z":"f6f2187d.f17ca8","name":"","rules":[{"t":"set","p":"items2","pt":"flow","to":"payload","tot":"msg","dc":true}],"action":"","property":"","from":"","to":"","reg":false,"x":1520,"y":480,"wires":[["c763ae08e1b98183"]]},{"id":"adc9b1b299e23d00","type":"switch","z":"f6f2187d.f17ca8","name":"","property":"$flowContext('items2')[id=$$.payload.id]","propertyType":"jsonata","rules":[{"t":"null"}],"checkall":"false","repair":true,"outputs":1,"x":1190,"y":440,"wires":[["9d49fd57a50d89da","8e9356ac6db955bb"]]},{"id":"c763ae08e1b98183","type":"debug","z":"f6f2187d.f17ca8","name":"Final","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1690,"y":440,"wires":[]}]

You could use a filter+some function to get the differences

In a function node;

const items = msg.payload.items
const changed = msg.payload.changed
const result = items.filter(a => !changed.some(b => (b.id === a.id && b.qty === a.qty)))

msg.payload.difference = result

return msg;

output:

{
    "items": [{
        "id": 1,
        "qty": 0
    }, {
        "id": 2,
        "qty": 2
    }, {
        "id": 3,
        "qty": 1
    }, {
        "id": 4,
        "qty": 0
    }, {
        "id": 5,
        "qty": 5
    }],
    "changed": [{
        "id": 1,
        "qty": 0
    }, {
        "id": 2,
        "qty": 0
    }, {
        "id": 3,
        "qty": 3
    }, {
        "id": 4,
        "qty": 0
    }, {
        "id": 5,
        "qty": 1
    }],
    "difference": [{
        "id": 2,
        "qty": 2
    }, {
        "id": 3,
        "qty": 1
    }, {
        "id": 5,
        "qty": 5
    }]
}

Example flow

[{"id":"f5c75d5369b36f29","type":"inject","z":"710286c1e53e083e","name":"items","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"items","payload":"[{\"id\":1,\"qty\":0},{\"id\":2,\"qty\":2},{\"id\":3,\"qty\":1},{\"id\":4,\"qty\":0},{\"id\":5,\"qty\":5}]","payloadType":"json","x":122,"y":216,"wires":[["d5beb3c749a419cd"]]},{"id":"79e82e6b9fd7a355","type":"inject","z":"710286c1e53e083e","name":"changed items","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"changed","payload":"[{\"id\":1,\"qty\":0},{\"id\":2,\"qty\":0},{\"id\":3,\"qty\":3},{\"id\":4,\"qty\":0},{\"id\":5,\"qty\":1}]","payloadType":"json","x":152,"y":288,"wires":[["d5beb3c749a419cd"]]},{"id":"0e774ed34aeb3001","type":"debug","z":"710286c1e53e083e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":602,"y":240,"wires":[]},{"id":"d5beb3c749a419cd","type":"join","z":"710286c1e53e083e","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":338,"y":240,"wires":[["72336c466dcd2a88"]]},{"id":"72336c466dcd2a88","type":"function","z":"710286c1e53e083e","name":"","func":"const items = msg.payload.items\nconst changed = msg.payload.changed\nconst result = items.filter(a => !changed.some(b => (b.id === a.id && b.qty === a.qty)))\n\nmsg.payload.difference = result\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":468,"y":240,"wires":[["0e774ed34aeb3001"]]}]
1 Like