How to build a flow variable array in a function

Hello. I am a little bit stuck trying to build a flow variable array inside a function. I need this variable to be flow or global so I can use it in other functions:

The function look like this

//add the values to the msg so they can be debugged and used later

quantity = msg.payload
var device_id = msg.topic.split("/");// split the whole topic to devicex ( untill /)
msg.payload = device_id[0];
msg.topic=`UPDATE pack_to_light SET Quantity = '${quantity}' WHERE Device = '${device_id[0]}'`


flow.set("individual_items",msg.payload);
node.warn(flow.individual_items)

return msg;

This function will be triggered multiple times (may vary). For example, if this function is gets triggered 3 times, every time its triggered I set a new payload value to it as such:

msg.payload = device_id[0];

I need to set this msg.payload to flow variable named "individual_devices". I have attempted to set "individual_devices" to msg.payload but I cant figure out how do I build array out of it since I need the result to be something like that ( If function is triggered 3 times)

flow.individual_devices[0] = msg.payload1
flow.individual_devices[1] = msg.payload2
flow.individual_devices[2] = msg.payload3

In short, how can I keep track how many times the function did execute so I can create a variable length array for " individual_devices"

Also, can I read flow type variable as such?:
flow.Individual_devices
or I must use flow.get function?
flow.get('Individual_devices' )

1 Like

Yes, use flow.get(... And flow.set(..

Thanks sir. I have managed to make slight progress:

//add the values to the msg so they can be debugged and used later
flow.set('internal_counter',0)
node.warn(flow.get('internal_counter'))
quantity = msg.payload
var device_id = msg.topic.split("/");// split the whole topic to devicex ( untill /)
msg.payload = device_id[0];
msg.topic=`UPDATE pack_to_light SET Quantity = '${quantity}' WHERE Device = '${device_id[0]}'`


//flow.set("individual_items",msg.payload);
//node.warn(flow.get('individual_items'))
flow.set('internal_counter',flow.get('internal_counter')+1)
node.warn(flow.get('internal_counter'))
return msg;

I have implemented internal counter inside function , the problem is that I have initially set this counter to 0, and then increment it, but when the function gets called another time, it sets it back to 0. Is it possible to declare internal_counter to 0 just once?

You could use something like var internal_counter = flow.get("internal_counter") || 0; and than use this var in your set statement instead of another get statement.
This way if the counter is not set yet it will be zero.

Could you tell me what exactly does || 0 does?

My current function;

var internal_counter = flow.get("internal_counter")||0;
flow.set("individual_items[internal_counter]",msg.payload);
node.warn(flow.get('individual_items[internal_counter]'))
flow.set('internal_counter',internal_counter+1)
node.warn(flow.get('internal_counter'))
return msg;

So now, the internal_counter is not set back to 0 evertyime the function is called but now, it just keeps incrementing the inernal_counter even when I restart the flow it still retains the last counter value. How would I make so that everytime the flow is deployed or restarted, the counter automatically goes to 0.

Also, do you spot any obvious errors in my function since I cant seem to set flow variable individual_items[internal_counter] because I am getting error:

23/07/2020, 11:11:14node: Update DB quantity when reached 0, set device names to global
function : (error)
"Error: Invalid property expression: unexpected i at position 17"
23/07/2020, 11:11:14node: Update DB quantity when reached 0, set device names to global
function : (error)
"Error: Invalid property expression: unexpected i at position 17"

I dont understand what it means by unexpected i at since I dont have any i in my function

should be

flow.set("individual_items['internal_counter']",msg.payload);

see the single quotations within the square bracket?

It initializes the variable to 0 if the flow var you are trying to get is undefined. To get it back to 0 you should set the flow var purposely to 0 once the counter is done.
You can initialize it to zero on nodered start by using a inject node set to fire once connected to a change node that sets the flow var to zero.

Thanks for your help.
Do we use single quotation marks to say that this is a variable?
double quotation marks would mean that this is a string?

var internal_counter = flow.get("internal_counter")||0;
flow.set("individual_items['internal_counter']",msg.payload);

node.warn(flow.get('individual_items["internal_counter"]'))

my node.warn does not execute properly - I think there is some problem with quotation marks?

WHat is the diference between

node.warn(flow.get('individual_items["internal_counter"]'))
and
node.warn(flow.get('individual_items['internal_counter']'))

(See the quotation marks near the "internal_counter")

The first example prints "undefined"

The second example wont even deploy becaue of:

I think I got wrong what you wanted to do. What I gave you was the syntax to access a property of the individual_items object called internal_counter, but its an array right(?) and you want to access an index in that array by the local internal_counter var?
Ok my fault :see_no_evil: sorry :bowing_man:
To use a local var as an array index in a get or set statement use string concatenation:

flow.set("individual_items[" + internal_counter + "]",msg.payload);

or you could also use a template string if you feel fancy:

flow.set(`individual_items[${internal_counter}]`,msg.payload);

Edit
Concerning the "" and '' in general in javascript if you want to use quotations within a string there is multiple ways. As javascript supports both as string annotations you can either use the opposite with what you annotated the string within the string. So either:

var test = 'this is "a test"';

or

var test = "this is 'a test'";

or you can escape the quotations with a backslash if you want to use the same:

var test = "this is \"a test\"";

Yes you are correct.

I am not able to node.warn print what individual_items are assigned to:
Trying many different ways but get many different errors

node.warn(flow.get('individual_items[internal_counter]'))
node.warn(flow.get('individual_items['internal_counter']'))
node.warn(flow.get('individual_items["internal_counter"]'))
node.warn(flow.get('individual_items["+internal_counter+"]'))
node.warn(flow.get("individual_items["+internal_counter+"]"))

The closes I got is using this function:

node.warn(flow.get("individual_items["+internal_counter+"]"))


As you can see from the debug menu, it returns first and second letter of the individual_items instead of returning first and second element of array

This one should work if:

  • a: individual_items is an array that is defined in flow.context
  • b: internal_counter is a valid index of individual_items

Have a look at this simple example and what you see in the debug tab when you inject:

[{"id":"4678cb7c.22a7d4","type":"function","z":"7782f553.5f6a14","name":"","func":"flow.set(\"test\",[0,0,0,0,0]);\nnode.warn(flow.get(\"test\"));\nvar num = 3;\nflow.set(`test[${num}]`,16);\nnode.warn(flow.get(\"test\"));\nnode.warn(flow.get(\"test[\" + num + \"]\"));\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":440,"y":1340,"wires":[[]]},{"id":"b1d94137.92985","type":"inject","z":"7782f553.5f6a14","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":1340,"wires":[["4678cb7c.22a7d4"]]}]

This is in the function node:

flow.set("test",[0,0,0,0,0]);
node.warn(flow.get("test"));
var num = 3;
flow.set(`test[${num}]`,16);
node.warn(flow.get("test"));
node.warn(flow.get("test[" + num + "]"));
return null;

and this is the output as expected:

Have you checked that you properly set your array flow variable individual_items?
What do you see in the context browser?
Like for my example above:

I have realized that I have not even initialized my individual_items array. Can I do that If I do not know the length of it?

I am just using flow.set to set it to msg.payload but I havent even declared the individual_items

// MISSING INDIVIDUAL_ITEMS INITIAL DECLARATION
var internal_counter = flow.get("internal_counter")||0;

flow.set("individual_items["+internal_counter+"]",msg.payload);
node.warn(flow.get("individual_items["+internal_counter+"]"))
flow.set('internal_counter',internal_counter+1)
node.warn(flow.get('internal_counter'))

return msg;

You can do the same trick as || 0 but use || [] but this will give you an empty array which doesnt change that internal_counter has to be a valid index within that array.
It looks to me like you should look at array methods and how javascript arrays actually work.
You can only set indexes in arrays that exist. To add to an array you will have to look at methods like array.push() and the same goes for removing array elements. You will have to use methods like slice() or pop(). Please do read this whole section about js array methods:

Before you dont understand the basics of this you will just run into one Roadblock after another and other people and me will have to feed you information bite by bite which will get very tedious for you and everybody else.

I have read the article -thanks.

However, it still not clear for me how do I set the flow variable to be array type.

Normally, I would just use:
var items_array = []
That would create a variable items_array of unknown size

The hard part for me is understanding the node-red sytax.

I have also read this article:
https://nodered.org/docs/user-guide/writing-functions
And could not find any hints on how to create a flow that would be array type.

You have mentioned using || [] and that sort of makes sense to me except that I am not sure how to use it? did you mean something like:

var items_array = flow.get("internal_counter") || []

Now my items_array will be a flow type variable array?

As I explained above this will initialize var items_array to be an empty array should flow.internal_counter be undefined / not initialized.
For flow.internal_counter to be an array you would than have to do a flow.set("internal_counter", items_array); at some point after the get statement as the || [] doesnt change the actual flow.internal_counter but just set var items_array to [] if the prior is undefined.

Okay, I just did
var items_array = [] ; flow.set("individual_items" ,items_array);
Now it lets me set the individual elements of a individual_items because it is set as an array.

Thank you very much. Got there in the end

Just make sure to only do this once / when the flow.var is undefined or you will reset the array to be empty every time you run the function.

var items_array =var items_array || []

Would this not be correct?

Since I am not holding any information in the items_array, I am just using this to set the flow variable as array

var items_array = []
//var items_array =  flow.get("individual_items")|| []
flow.set("individual_items",items_array);

flow.set("individual_items["+internal_counter+"]",msg.payload);
node.warn(flow.get("individual_items["+internal_counter+"]"))
flow.set('internal_counter',internal_counter+1)
node.warn(flow.get('internal_counter'))