Global numeric array trouble using global get and set

I am doing just that and yes it WAS my understanding (though I’d forgotten) that strings and arrays should not need the final step as they aert pass by reference however I’m taking your advice and copying the local back to the global.

Can you post the code that doesn't work so we can see?

The array method unshift modifies the array itself, but splice does not, it returns the new array. So I was only half right anyway.

However, I agree with @knolleary that my suggestion is not good practice. I don't think my comment should be ignored as it is useful to understand how things work, but probably best not to do it that way anyway

As a matter of fact:

array.splice() - mutates the original array
array.slice() - does not mutate the original array

I would go with pop() instead of splice / slice anyways

It works fine

[{"id":"d212e559.d6e8b8","type":"tab","label":"Array - Shift ","disabled":false,"info":""},{"id":"c0b39527.67aa38","type":"inject","z":"d212e559.d6e8b8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":210,"y":180,"wires":[["2586acb.e0f4c54"]]},{"id":"2586acb.e0f4c54","type":"function","z":"d212e559.d6e8b8","name":"Set and display Global array","func":"let arr = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23];\nglobal.set(\"garr\",arr);\nnode.warn(global.get(\"garr\"));\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":180,"wires":[[]]},{"id":"c9a83478.a5dbb8","type":"inject","z":"d212e559.d6e8b8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":200,"y":300,"wires":[["2e7ca88c.dc6058"]]},{"id":"2e7ca88c.dc6058","type":"function","z":"d212e559.d6e8b8","name":"Shift Global array adding random value","func":"let rand = msg.payload%24;\n\n// read array from global, process and store againt o global\nlet arr = global.get(\"garr\");\narr.unshift(rand);\narr.pop();\nglobal.set(\"garr\",arr);\n\n// Display actual global array\nnode.warn(global.get(\"garr\"));\nreturn msg;","outputs":1,"noerr":0,"x":490,"y":300,"wires":[[]]},{"id":"da680b07.b8e498","type":"comment","z":"d212e559.d6e8b8","name":"Click inject node to shift array","info":"","x":250,"y":260,"wires":[]}]

Of course, brain fade setting in here I think.

I would have expected that to work except where 'hum' is not already setup. If the || [] is invoked then that does not set the global up so it will not work. Does it work if the global does contain an array? If not does it work if you add a global.set at the end?

Here is the flow I tested, actually it only doesn't work if the array is undefined. But I think defensive programming is anyway a good method so I 'd use global.set()

[{"id":"d6547d88.1430f","type":"inject","z":"12f1321f.454d1e","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":120,"wires":[["e8392f2d.46568"]]},{"id":"ea00bc55.eddb2","type":"debug","z":"12f1321f.454d1e","name":"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":590,"y":120,"wires":[]},{"id":"e8392f2d.46568","type":"random","z":"12f1321f.454d1e","name":"","low":"20","high":"80","inte":"true","property":"payload","x":260,"y":120,"wires":[["ef4fe1a1.551f2"]]},{"id":"ef4fe1a1.551f2","type":"function","z":"12f1321f.454d1e","name":"hum array","func":"var hum = global.get('hum') || [];\n\nhum.unshift(msg.payload);\nhum.splice(24, 1);\n\n//global.set('hum', hum);\n\nmsg.payload=hum;\n\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":120,"wires":[["ea00bc55.eddb2"]]}]

I agree, but it is always good to understand why things do or don't work.

Pete, I think the confusion is because some of the array methods do not return the original array, but a new copy of the array, or even the count of the new objects (a number) -- as mentioned on this W3 Schools page:

The unshift() method adds a new element to an array (at the beginning), and "unshifts" older elements:

Example
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.unshift("Lemon");    // Adds a new element "Lemon" to fruits

The unshift() method returns the new array length.

So it's best to use global.get to get a reference to the global array, then you can operate on it locally, and finally use global.set to "store" it back into the global space. As Nick mentioned, that also has the advantage of avoiding any side-effects once context persistence is no longer memory based.

I also had another thought, which might make sense for your specific case: you could add a named empty array to the functionGlobalContext section of your settings.js file. This would (I think) allow you to access the array from any function node (but not change nodes) in the the manner that you have been using. I'm not saying that it's a good idea, but it could work. It's just that this feels more like a configuration library array than a typical "global" runtime variable...

Nick, do you think that using functionGlobalContext for initial data storage is bad advice? Are there some potential issues with that idea?

Hi

I ended up using a local array updated from the global one and saving to the global at the end. That works and I’m happy with that, none of which explains why I could not do the maths directly on the global array.. I’ve done math on hundreds of global integers without needing a local copy. However as I didn’t keep the original global code I guess I’ll never find out what was wrong.

The somewhat badly named unshift method works a treat.

Pete

Well there is push and pop that work on the end of an array, and shift and unshift that work on the front of an array so you can use whichever pair(s) makes most sense depending on which way you want to build your array.

Did you use global.get() or just context.global.myvar?

Hi there

Unshift work a treat.. I don’t worry about resizing the local array as it gets recreated every time I call the function and I just use the first 24 values. The challenge of sticking with the global still alludes me. How would you use unshift with the GLOBAL array? Let’s say you were poking a global value into position zero…. If anyone is up for this challenge.. I have a local answer but it’s annoying the hell out of me that I can’t figure out how to do this with the global array.

Essentially it is a 24 value array. Every hour I need to unshift a value into position zero and resize the array to keep the length at 24 (hence reading the last 24 values. I read all 24 and divide by 24 to get the average humidity. A complication is that a few hours after the program starts there will not be 24 values so I need to use however many there are and divide by that number (the array on powerup is initialized to all values = -1).

I tried to do all of this on the original global array without using a local, and failed miserably.

Pete

The only operations we provide are get and set. To modify a value in context you have to get it, modify it and then set it back. That's it.

Can you provide some code that shows (even if it doesn't work) what you think you want to be able to do?

Global.get and set, that is how I always use global vars

global.set(“blim”,[]);

for (var a=0 a<10;a++) global.set(“blim[“ + a + “]”,-1);

It really doesn’t get any simpler. THAT much WORKS – Just done it. I set up the entire array with value -1 without issue, one part at a time.

However, adding in a little extra KILLS NODE-RED – the only way out is a reboot

var a;

global.set("blim",[]);

for (a=0;a<10;a) global.set("blim[" + a + "]",-1);

msg.payload="";

for (a=0;a<10;a++) msg.payload+="_ " + global.get("blim[" + a + "]");

return msg;

When I say it works, it may actually do some damage as after all of the above I lost all my NR credentials. Thank heavens for backups.

As is becoming apparent, NR has no protection against abuse of global vars and apparently just gives up and dies when that happens.

I just gave an example that killed ny NR setup.

Pete

You killed Node-RED by not incrementing a in this for loop - causing it to loop forever.

1 Like

Oh, good grief – you are of course absolutely right Nick – my apologies. Ok that works. Back to how to unshift and pop

Hence below works except for the unshift line. I tried a couple of variations..

var a;

global.set("blim",[]);

for (a=0;a<10;a++) global.set("blim[" + a + "]",-1);

msg.payload="";

for (a=0;a<10;a++) msg.payload+="_ " + global.get("blim[" + a + "]");

node.send (msg);

//global.set("blim",global.get("blim.pop()"));

global.set("blim",global.get("blim.unshift("+67+")"));

for (a=0;a<10;a++) msg.payload+="_ " + global.get("blim[" + a + "]");

return msg;