Wednesday, June 13, 2018

Join node and the reduce method - 2

This flow is a variation of the previous one. Here we want to sum up the values from each sensor.  This flow could be used, with a minor change, to calculate the average of the values from the sensors. Using averaged values is common in many real applications.

Here is an example of the flow result, after pressing the inject nodes five times  in a random way:



This is how the flow looks like:



The main part is the use of the reduce method inside the function node. See below the code used:

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

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


The way the reduce method works has been explained in the previous post. Basically, it will initialize the accumulator with an empty object and apply the function reducer for each element of the array.

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

 During the iteration, each object from the array is read. If the sensor is not already included in the accumulator it will be included. The only difference is that we include now the value from the reading instead of including 1 (since we are not counting the number of readings but summing up the total of the values). Using the example from the first picture, the first object added to the accumulator will be :

{sensor1 : 8}


In this iteration logic if the sensor name is already included then the value of its key is added to the value already accumulated. The second iteration from this example uses the object :

{sensor1: 4}

Therefore the value 4 is added to the value that is already accumulated for this key, resulting in the sum of 12.


Flow:

[{"id":"2adc0577.04543a","type":"tab","label":"13-Jun-2018","disabled":false,"info":""},{"id":"909eac2c.7449f","type":"inject","z":"2adc0577.04543a","name":"","topic":"sensor1","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":152.09999465942383,"y":76,"wires":[["77c5479a.4b6b38"]]},{"id":"77c5479a.4b6b38","type":"function","z":"2adc0577.04543a","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":382.1000633239746,"y":154.99999904632568,"wires":[["7b0a0c28.249564"]]},{"id":"c086ba71.7876c8","type":"inject","z":"2adc0577.04543a","name":"","topic":"sensor2","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":149.99999237060547,"y":129.99999523162842,"wires":[["77c5479a.4b6b38"]]},{"id":"1b077c6c.b30cf4","type":"inject","z":"2adc0577.04543a","name":"","topic":"sensor3","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":149.99999237060547,"y":181.99999523162842,"wires":[["77c5479a.4b6b38"]]},{"id":"a5556ca6.334d8","type":"inject","z":"2adc0577.04543a","name":"","topic":"sensor4","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":145.99999237060547,"y":232.99999523162842,"wires":[["77c5479a.4b6b38"]]},{"id":"7b0a0c28.249564","type":"join","z":"2adc0577.04543a","name":"Join 5","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":556.1000595092773,"y":153.00000476837158,"wires":[["cea93a9c.463a98","c430db80.0d25b8"]]},{"id":"20ec7a2c.c08876","type":"debug","z":"2adc0577.04543a","name":"Sumup events","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":895.9000129699707,"y":153.00001049041748,"wires":[]},{"id":"cea93a9c.463a98","type":"function","z":"2adc0577.04543a","name":"Sumup events","func":"let reducer2 = function (elemlist, elem) {\nlet key = Object.keys(elem);\nlet value = elem[key];\n(key in elemlist) ? elemlist[key]+= value : elemlist[key] = value;\nreturn elemlist;\n}\n\nlet payload = msg.payload.reduce(reducer2, {});\nmsg.payload = payload;\nreturn msg;","outputs":1,"noerr":0,"x":712.9000091552734,"y":154.00000381469727,"wires":[["20ec7a2c.c08876"]]},{"id":"c430db80.0d25b8","type":"debug","z":"2adc0577.04543a","name":"All events","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":703.0000114440918,"y":78.00000095367432,"wires":[]}]




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"]]}]

Monday, June 11, 2018

A simple use of the join node

This is a very simple flow built on the top of a previous post .

It uses a Node-RED join node to group sequences os messages received from sensors. The output of the join node will give us an array with a fixed number of elements. This number of elements is configurable.




We will select the manual mode and set the number of messages to, let´s say five.




Here is like the flow looks like:



and this is what the flow shows in the debug node after you press the inject buttons a number of times:



As we expected the outcome is an array of five elements inside the message payload.


This array will allow us to perform some interesting calculations, like averaging values, checking the number of readings from each sensor, spot readings above the average, triggering other events, etc.


Flow:

[{"id":"369eef7d.03645","type":"tab","label":"Flow 18","disabled":false,"info":""},{"id":"ccb23d2.10b15c","type":"inject","z":"369eef7d.03645","name":"","topic":"sensor1","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":156,"y":107,"wires":[["1fe33550.256e1b"]]},{"id":"1fe33550.256e1b","type":"function","z":"369eef7d.03645","name":"Format sensor output","func":"let value = Math.floor(Math.random() * msg.payload + 1)\nmsg.payload = {[msg.topic] : value};\n//delete msg.topic;\nreturn msg;","outputs":1,"noerr":0,"x":386.0000686645508,"y":185.99999904632568,"wires":[["d25aaeae.def1f"]]},{"id":"b06bf348.e7bac","type":"inject","z":"369eef7d.03645","name":"","topic":"sensor2","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":153.89999771118164,"y":160.99999523162842,"wires":[["1fe33550.256e1b"]]},{"id":"ce089477.49c8a8","type":"inject","z":"369eef7d.03645","name":"","topic":"sensor3","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":153.89999771118164,"y":212.99999523162842,"wires":[["1fe33550.256e1b"]]},{"id":"a543c32d.d11d7","type":"inject","z":"369eef7d.03645","name":"","topic":"sensor4","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":149.89999771118164,"y":263.9999952316284,"wires":[["1fe33550.256e1b"]]},{"id":"f18969a7.7a9c78","type":"debug","z":"369eef7d.03645","name":"All events","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":706.8000106811523,"y":186.00000381469727,"wires":[]},{"id":"d25aaeae.def1f","type":"join","z":"369eef7d.03645","name":"Join 5","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":568.9001045227051,"y":185.00000286102295,"wires":[["f18969a7.7a9c78"]]}]

Sunday, June 10, 2018

Injecting random numbers, undefined or null

The inject node is very handy but, at the time of this writing, it lacks the capability to inject some kind of values.

This is what the node is capable of injecting in a flow:



What if you want to inject a random integer number, or the undefined value or the null value?

You can´t. It will be necessary to an additional function node. So I did.

This is the code inside the function node:

const MIN = 1;
const MAX = 99;
//The maximum is exclusive and the minimum is inclusive
switch (msg.payload) {

case "undefined" :
msg.payload = undefined;
break;

case "null" :
msg.payload = null;
break;

case "random" :
msg.payload =function getRandomInt(min = MIN, max = MAX) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}();
break;
}
return msg;


It requires that the minimum and maximum values of random numbers be hardcoded in the first line of the function.

The undefined and null values that will be passed in the  msg.payload will be really undefined and null, not strings.



Here a screenshot of the debug panel after pressing a few times the inject nodes.




Here the flow:


[{"id":"52e18ccb.c4d624","type":"inject","z":"391a528.22394ae","name":"","topic":"","payload":"random","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":229.0999984741211,"y":155.00000190734863,"wires":[["26b163fc.3928ec"]]},{"id":"26b163fc.3928ec","type":"function","z":"391a528.22394ae","name":"random, undefined, null","func":"const MIN = 1;\nconst MAX = 99;\n//The maximum is exclusive and the minimum is inclusive\n \nswitch (msg.payload) {\n\ncase \"undefined\" :\nmsg.payload = undefined;\nbreak;\n\ncase \"null\" :\nmsg.payload = null;\nbreak;\n\ncase  \"random\" :\nmsg.payload =function getRandomInt(min = MIN, max = MAX) {\n  min = Math.ceil(min);\n  max = Math.floor(max);\n  return Math.floor(Math.random() * (max - min)) + min;\n}();\nbreak;\n}\nreturn msg;\n\n","outputs":1,"noerr":0,"x":421.1000747680664,"y":154.00000190734863,"wires":[["325c68cb.b409a8"]]},{"id":"325c68cb.b409a8","type":"debug","z":"391a528.22394ae","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":632.1000595092773,"y":154.00000190734863,"wires":[]},{"id":"ae52d302.9a904","type":"inject","z":"391a528.22394ae","name":"","topic":"","payload":"null","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":228,"y":205,"wires":[["26b163fc.3928ec"]]},{"id":"b7d139ff.0bec58","type":"inject","z":"391a528.22394ae","name":"","topic":"","payload":"undefined","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":221,"y":105.00000095367432,"wires":[["26b163fc.3928ec"]]}]



Saturday, June 9, 2018

Injecting data for testing flows

Many times we need to inject fake or random data to test our flows. I remind you that there are some Node-RED nodes that need a sequence of messages to produce its work, like the join node, the batch node, and the sort node.

I document here a piece of flow I have used recently.

The desired format was a msg object with this structure:

{"sensor1" : 8}


The value should be random and the sensor name should be fixed but configurable.

This is how the flow looks like:


Each inject node will generate a random integer number from 1 to 9. The upper limit can be changed by adjusting the content of msg.payload in the configuration dialog box of the node. Note that the name of the sensor is configured as the topic of the message as it will be used in the following node.




The function node will generate the random values and will format the msg.payload to the desired format.

Here is the code inside the function node:

let value = Math.floor(Math.random() * msg.payload + 1)
msg.payload = {[ msg.topic ] : value};
return msg;

Note here the use of the destructuring pattern with computed values. In this case, the computed value is whatever is the value in msg.topic.

Here is an output from the debug node after randomly pressing different node buttons:




Flow:

[{"id":"c22aa68d.3dd668","type":"inject","z":"1f275547.b32ebb","name":"","topic":"sensor1","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":124.10000610351562,"y":67.00000476837158,"wires":[["726a491e.723d58"]]},{"id":"726a491e.723d58","type":"function","z":"1f275547.b32ebb","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":354.1000747680664,"y":146.00000381469727,"wires":[["706fefa4.1fe6a"]]},{"id":"991eae59.37e98","type":"inject","z":"1f275547.b32ebb","name":"","topic":"sensor2","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":122.00000381469727,"y":121,"wires":[["726a491e.723d58"]]},{"id":"91074579.4b1d58","type":"inject","z":"1f275547.b32ebb","name":"","topic":"sensor3","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":122.00000381469727,"y":173,"wires":[["726a491e.723d58"]]},{"id":"f4a4b20b.45016","type":"inject","z":"1f275547.b32ebb","name":"","topic":"sensor4","payload":"9","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":118.00000381469727,"y":224,"wires":[["726a491e.723d58"]]},{"id":"706fefa4.1fe6a","type":"debug","z":"1f275547.b32ebb","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":559.1000366210938,"y":145,"wires":[]}]



Friday, June 8, 2018

Working with messages - part 3

Here is an interesting use case. Now we want to generate the object like below:

[{"name1":1},{"name2":2},{"name3":3}]

However, the original data is no longer other object but instead a couple  of arrays, like below:

let arrName = ["name1","name2","name3"];
let arrNumber = [1,2,3];

I can imagine many different solutions, let´s use the one that seems to be the simplest.


It is just a matter of combining the pairs name and value from each array. It could be done with a while loop to iterate over the arrays, the method array.shift to extract the elements from the arrays, the method array.push to load them into a temporary array. It will be necessary to combine the pair name and value into a single object. This last part could be achieved by using the property value shorthand that goes like this:

 { name: name, value: value}  can be coded  {name, value}, no need to repeat the variable name if it is the same as the key in the object. 


Therefore the code inside the function node would be as follows:

let outputMsg = [];
let arrName = ["name1","name2","name3"];
let arrNumber = [1,2,3];

while (arrName.length > 0) {
name = arrName.shift();
value = arrNumber.shift();
outputMsg.push({ name, value });
}

msg.payload = outputMsg;
return msg;



and this is how the debug node will display the results in the side panel of Node-Red;





Node-Red flow:

[{"id":"74cfb3c0.34942c","type":"inject","z":"43f25cb3.43e2d4","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":303,"y":285,"wires":[["d79853f4.ef2cc"]]},{"id":"544646e7.ce3208","type":"debug","z":"43f25cb3.43e2d4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":691.0000076293945,"y":285.00000381469727,"wires":[]},{"id":"d79853f4.ef2cc","type":"function","z":"43f25cb3.43e2d4","name":"Two Arrays to Object","func":"let outputMsg = [];\nlet arrName = [\"name1\",\"name2\",\"name3\"];\nlet arrNumber = [1,2,3];\n\nwhile (arrName.length > 0) {\nname =  arrName.shift(); \nvalue = arrNumber.shift();\noutputMsg.push({name, value});\n}\n\nmsg.payload = outputMsg;\nreturn msg;","outputs":1,"noerr":0,"x":499.99999809265137,"y":286,"wires":[["544646e7.ce3208"]]}]


Thursday, June 7, 2018

Working with messages - part 2

Let's say we want to perform a transformation that is the opposite of the one shown in the previous post. 

In such case the input msg.payload is :


[{"name1":1},{"name2":2},{"name3":3}]

and we want the following array as the outcome:

[{"Name":"name1","Number":1},{"Name":"name2","Number":2},{"Name":"name3","Number":3}]

I have to say that the solution is not trivial but at the end of the day the final code will be very concise thanks to the use of array destructuring.


As usual, we will need to loop over the original array and take each element in separate for an additional processing. The first iteration of the loop will provide us the following object: 


{ name1: 1 }

The thing here is: how we can possibly handle the key given that it will change in each iteration of the loop?

It is not possible to use object destructuring as this would require knowing the key name in advance. So, this would not work:

let {Name} = {"name1" : 1};
console.log(Name);

The variable Name would get the value undefined.

We have to resort to a special method from JavaScript to extract each the keys and the values from the original array. This is the method Object.entries()

The only problem is that this method will return an array of the keys and values, as shown below:


let payload = [{"name1":1},{"name2":2},{"name3":3}];
for (let elem of payload) {
let a = Object.entries(elem);
console.log(a);



and the following is displayed in the console:


[ [ 'name1', 1 ] ]
[ [ 'name2', 2 ] ]
[ [ 'name3', 3 ] ]

Each loop iteration will return an array and each of these arrays contains another array with the pair [key, value]

Sounds like this is getting complicated, right?  Fortunately, the use of the array destructuring will get us back to the track.

We can extract the key and the value using the array destructuring shown in the left side of the equal sign:

[ [ a,b ] ] = [ [  "name1" , '1 ] ]

which assigns a = "name1" and b = 1


Finally, this is the code to use inside the node function :


let arr = [];

for (let elem of msg.payload) {
let [[a,b]] = Object.entries(elem);
arr.push({"Name": a, "Number": b});
}

msg.payload = arr;
return msg;


and this is what the debug node will display:






Flow:


[{"id":"eb3b9fba.c051d","type":"debug","z":"e57cf4d8.3c8218","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":558,"y":512,"wires":[]},{"id":"c656406b.904d6","type":"function","z":"e57cf4d8.3c8218","name":"For of loop","func":"let arr = [];\n\nfor (let elem of msg.payload) {\nlet [[a,b]] = Object.entries(elem);\narr.push({\"Name\": a, \"Number\": b});\n}\n\nmsg.payload = arr;\nreturn msg;","outputs":1,"noerr":0,"x":412.99999237060547,"y":511.99999809265137,"wires":[["eb3b9fba.c051d"]]},{"id":"bafa5144.bec81","type":"inject","z":"e57cf4d8.3c8218","name":"","topic":"","payload":"[{\"name1\":1},{\"name2\":2},{\"name3\":3}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240.99999237060547,"y":512,"wires":[["c656406b.904d6"]]}]




Wednesday, June 6, 2018

Working with messages

If you want to develop with Node-RED the very first thing you should understand is how to work with messages.  I consider the chapter "Working with Messages" from the Node-RED guide as the most important one for the beginner.

Almost always our flows will modify the msg object that is passed along the nodes. Node-RED has the powerful change node that help us to fit the msg to our needs. However , it is likely that a more complex msg object will require using a function node for that purpose. I show below a use case posted in the Node-RED forum.

Simply stated, convert below object:

[{"Name":"name1","Number":1},{"Name":"name2","Number":2},{"Name":"name3","Number":3}]

to the new object:

[{"name1":1},{"name2":2},{"name3":3}]

First thing to do is to understand the nature of the objects. Note that our initial object is an array of objects. Each element of the array is an object by itself.


{"Name":"name1","Number":1}

{"Name":"name2","Number":2}

{"Name":"name3","Number":3}

Each of these objects have the keys: Name and Number. The desired output is also an array of objects. Each object of the new array will be built using the values from the initial object. The goal is to build a new object using only the property values, therefore discarding the keys Name and Number.

There are many possible ways to write a code tho accomplish such transformation. As you can imagine they will require some iteration (looping).

I describe here four different solutions. All of them using this basic flow: inject node + function node + debug node






First solution: Using the map method


The map method will create a new array by taking each and every element of the original array and processing them with a function code.


This is the JavaScript code inside the function node:

let arr= msg.payload.map(elem => ({[ elem.Name ] :elem.Number}));
msg.payload = arr;
return msg;

The map method will visit each element of the array and will return a modified object, so in the first iteration:

elem = {"Name":"name1","Number":1}

generates:  {"name1":1}

Note that the function in the map method (by the way an arrow function) returns an object in the literal notation {"name1":1} thanks to the use of the square brackets surrounding [elem.Name]. Whenever there is a pair of square brackets like this the variable inside brackets is computed. In this example it simply returns the value of the variable elem.Name which happens to be "name1"

You can learn more on computed properties by clicking in this link.

This is how the debug node displays the result f the flow:



and here is the flow in case you want to import and test:

[{"id":"76c98300.8f224c","type":"debug","z":"d61a402.f83d6c","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":496,"y":191.0000057220459,"wires":[]},{"id":"af7dbb81.8640a8","type":"function","z":"d61a402.f83d6c","name":"map method","func":"let arr= msg.payload.map(elem => ({[elem.Name] :elem.Number}));\nmsg.payload = arr;\nreturn msg;\n","outputs":1,"noerr":0,"x":334.9999828338623,"y":190,"wires":[["76c98300.8f224c"]]},{"id":"c50940a3.ee025","type":"inject","z":"d61a402.f83d6c","name":"","topic":"","payload":"[{\"Name\":\"name1\",\"Number\":1},{\"Name\":\"name2\",\"Number\":2},{\"Name\":\"name3\",\"Number\":3}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":162.9999828338623,"y":190.00000190734863,"wires":[["af7dbb81.8640a8"]]}]



Second solution: using the for of loop

The for loop is my preferred method to iterate arrays.

This would be the code inside the function node:

let arr = [];
for (let elem of msg.payload) {
arr.push({[ elem.Name ] :elem.Number});
}
msg.payload = arr;
return msg;

The for of loop visits each element of the array, modifies it and push the newly created object to the temporary array named arr. Later this temporary array will be copied to msg.payload.


The exported flow:

[{"id":"e60806b0.8821c8","type":"debug","z":"d61a402.f83d6c","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":540.0000076293945,"y":147,"wires":[]},{"id":"fb75ae0.2e3655","type":"function","z":"d61a402.f83d6c","name":"For of loop","func":"let arr = [];\n\nfor (let elem of msg.payload) {\n    arr.push({[elem.Name] :elem.Number});\n}\n\nmsg.payload = arr;\n\nreturn msg;","outputs":1,"noerr":0,"x":395,"y":146.99999809265137,"wires":[["e60806b0.8821c8"]]},{"id":"18252e58.62eae2","type":"inject","z":"d61a402.f83d6c","name":"","topic":"","payload":"[{\"Name\":\"name1\",\"Number\":1},{\"Name\":\"name2\",\"Number\":2},{\"Name\":\"name3\",\"Number\":3}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":223,"y":147,"wires":[["fb75ae0.2e3655"]]}]



Third solution: for loop + object destructuring

Same as the previous one with a small change.

let arr = [];
for (let { Name, Number } of msg.payload) {
arr.push({[ Name ] : Number});
}
msg.payload = arr;
return msg;


Here we use object destructuring inside the for of loop to load the variables Name and Number. Those variables will be used right after to create the modified object. We use also computed properties to generate the new objects (square brackets  in the literal object).


Here the Node-RED flow:

[{"id":"199fa305.6b864d","type":"debug","z":"d61a402.f83d6c","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":679,"y":146,"wires":[]},{"id":"4c5fb024.2cac","type":"function","z":"d61a402.f83d6c","name":"Destructuring inside the For of loop ","func":"let arr = [];\n\nfor (let {Name, Number} of msg.payload) {\n    arr.push({[Name] : Number});\n}\n\nmsg.payload = arr;\n\nreturn msg;","outputs":1,"noerr":0,"x":448.99999046325684,"y":146.00000190734863,"wires":[["199fa305.6b864d"]]},{"id":"773b7c94.e78794","type":"inject","z":"d61a402.f83d6c","name":"","topic":"","payload":"[{\"Name\":\"name1\",\"Number\":1},{\"Name\":\"name2\",\"Number\":2},{\"Name\":\"name3\",\"Number\":3}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":217.9999828338623,"y":147.9999942779541,"wires":[["4c5fb024.2cac"]]}]



Fourth solution: forEach method


let arr= [];
msg.payload.forEach(elem => arr.push({[ elem.Name ] :elem.Number}));
msg.payload = arr;
return msg;

The idea is the same as the previous solutions. The loop will visit each element of the original array and will have it modified by the callback function. The callback function just push the newly created object to the temporary array.


Node-RED flow:

[{"id":"29f9e29f.974c3e","type":"debug","z":"d61a402.f83d6c","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":555,"y":198,"wires":[]},{"id":"ac334e7d.3faab","type":"function","z":"d61a402.f83d6c","name":"forEach method","func":"let arr= [];\n\nmsg.payload.forEach(elem => arr.push({[elem.Name] :elem.Number}));\n\nmsg.payload = arr;\n\nreturn msg;","outputs":1,"noerr":0,"x":403.9999828338623,"y":196.9999942779541,"wires":[["29f9e29f.974c3e"]]},{"id":"3878f08.c4dc91","type":"inject","z":"d61a402.f83d6c","name":"","topic":"","payload":"[{\"Name\":\"name1\",\"Number\":1},{\"Name\":\"name2\",\"Number\":2},{\"Name\":\"name3\",\"Number\":3}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":221.9999828338623,"y":196.99999618530273,"wires":[["ac334e7d.3faab"]]}]