Tuesday, June 12, 2018

Join node node and reduce method

This flow shows how to use the array reduce method from JavaScript to generate an object that shows how many messages from a specific sensor have been grouped by the join node.

This is how the flow looks like:



This is how it will output the results in the debug sidebar after pressing the inject buttons randomly, several times:


This is the code inside the function node:

let reducer1 = function (elemlist, elem) {
let key = Object.keys(elem);
(key in elemlist) ? elemlist[key]++ : elemlist[key] = 1;
return elemlist;
}

let payload = msg.payload.reduce(reducer1, {});
msg.payload = payload;
return msg;


The code uses the method array.reduce to take the elements from msg.payload and store the result of the processing in the variable named payload (not in msg.payload).

The use of the reduce method is straightforward, just a function, here named reducer1, as the first parameter and an empty object (accumulator) as the second parameter.

let payload = msg.payload.reduce(reducer1, {});

The complex part is the function reducer1, perhaps.

let reducer1 = function (elemlist, elem) {
let key = Object.keys(elem);
(key in elemlist) ? elemlist[key]++ : elemlist[key] = 1;
return elemlist;
}

How this function works ?  It will iterate over each element of the array.  The picture below shows how the iteration happens (I have added a node.warn statement inside the function to show each and every element from the original array).



The keys of the object will be extracted to the array named key, so it will look like this:

["sensor2", "sensor3", "sensor1", "sensor4", "sensor4", ]

The the following line will check each element individually. This is important to understand. In the very first iteration, the parameter elemlist will take the value { } and the parameter elem will take the value of the first key, in this case "sensor2".

(key in elemlist) ? elemlist[key]++ : elemlist[key] = 1;

If the key is already inserted in the accumulator (which will never happen for the first occurrence of the element) an object will be inserted in the accumulator. The inserted object, for this example, will be

{"sensor2" : 1} and it will become the new elemlist

In the next iteration the accumulator will be:

{"sensor2" : 1, "sensor3" : 1} , which will be the new elemenlist

and then:

{"sensor2" : 1, "sensor3" : 1, "sensor1" : 1}, which will be the new elemenlist

and:

{"sensor2" : 1, "sensor3" : 1, "sensor1" : 1, "sensor4" : 1}, which will be the new elemenlist

and finally, as we will read an element with the key "sensor4", which already exist in the accumulator (it is not the first occurrence), the following statement will be executed:

elemlist[key]++

resulting in an increase of the value of property "sensor4".

This will be the final value accumulated:

{"sensor2" : 1, "sensor3" : 1, "sensor1" : 1, "sensor4" : 2} , final elemenlist

Therefore this is the final result from the function reducer1. It will be returned to the variable payload.

As the conclusion, We have used the array.reduce method to change an array to an object and this object shows how many times each sensor was read.





Flow:

[{"id":"c3f7e5fc.51d8a8","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"e54c2d56.b4373","type":"debug","z":"c3f7e5fc.51d8a8","name":"Count events","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":871.0000076293945,"y":159.00000667572021,"wires":[]},{"id":"5db8fd85.cf2d64","type":"function","z":"c3f7e5fc.51d8a8","name":"Count events","func":"let reducer1 = function (elemlist, elem) {\nlet key = Object.keys(elem);\n(key in elemlist) ? elemlist[key]++ : elemlist[key] = 1;\nreturn elemlist;\n}\n\nlet payload = msg.payload.reduce(reducer1, {});\nmsg.payload = payload;\nreturn msg;","outputs":1,"noerr":0,"x":685.0000114440918,"y":159.0000057220459,"wires":[["e54c2d56.b4373"]]},{"id":"bfb9ebec.b22cf8","type":"inject","z":"c3f7e5fc.51d8a8","name":"","topic":"sensor1","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140.10000228881836,"y":82,"wires":[["f19f97df.08a298"]]},{"id":"f19f97df.08a298","type":"function","z":"c3f7e5fc.51d8a8","name":"Format sensor output","func":"let value = Math.floor(Math.random() * msg.payload + 1)\nmsg.payload = {[msg.topic] : value};\ndelete msg.topic;\nreturn msg;","outputs":1,"noerr":0,"x":370.10007095336914,"y":160.99999904632568,"wires":[["200ed8a4.a8eb58"]]},{"id":"2fa20c0b.757084","type":"inject","z":"c3f7e5fc.51d8a8","name":"","topic":"sensor2","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":138,"y":135.99999523162842,"wires":[["f19f97df.08a298"]]},{"id":"f647d792.a74638","type":"inject","z":"c3f7e5fc.51d8a8","name":"","topic":"sensor3","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":138,"y":187.99999523162842,"wires":[["f19f97df.08a298"]]},{"id":"82b52883.091b98","type":"inject","z":"c3f7e5fc.51d8a8","name":"","topic":"sensor4","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":134,"y":238.99999523162842,"wires":[["f19f97df.08a298"]]},{"id":"200ed8a4.a8eb58","type":"join","z":"c3f7e5fc.51d8a8","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"5","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":541.1000556945801,"y":160.0000057220459,"wires":[["5db8fd85.cf2d64"]]}]