Why does that dynamic selector select both spotlights?

Hi
I use a dynamic selector to pick my device in a flow object by name.
But it changes the offset values of all devices.

let init = {
    "offset": 0
}
let spotlights = flow.get("spotlights") ?? flow.set("spotlights", {
    "spot1" : init,
    "spot2" : init
});

spotlights[msg.device].offset = msg.offset;
msg.payload = spotlights[msg.device];
return msg;

Test export:

[
    {
        "id": "96be0d97663c2db7",
        "type": "inject",
        "z": "cbf7f47c.caccc8",
        "name": "set spot2 offset",
        "props": [
            {
                "p": "device",
                "v": "spot2",
                "vt": "str"
            },
            {
                "p": "offset",
                "v": "15",
                "vt": "num"
            },
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "true",
        "payloadType": "bool",
        "x": 2940,
        "y": 4120,
        "wires": [
            [
                "1cde18b38154698e"
            ]
        ]
    },
    {
        "id": "1cde18b38154698e",
        "type": "function",
        "z": "cbf7f47c.caccc8",
        "name": "init flow.spotlights[device][offset]",
        "func": "let init = {\n    \"dimm\": { \"value\": 0, \"channel\": 1 },\n    \"nc1\": { \"value\": 0, \"channel\": 2 },\n    \"nc2\": { \"value\": 255, \"channel\": 3 },\n    \"nc3\": { \"value\": 0, \"channel\": 4 },\n    \"nc4\": { \"value\": 0, \"channel\": 5 },\n    \"temp\": { \"value\": 0, \"channel\": 6 },\n    \"zoom\": { \"value\": 0, \"channel\": 7 },\n    \"offset\": 0\n}\nlet spotlights = flow.get(\"spotlights\") ?? flow.set(\"spotlights\", {\n    \"spot1\" : init,\n    \"spot2\" : init\n});\n\nspotlights[msg.device].offset = msg.offset;\nmsg.payload = spotlights[msg.device];\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 3230,
        "y": 4100,
        "wires": [
            [
                "224767e32291753f"
            ]
        ]
    },
    {
        "id": "06811ce1a75b8c24",
        "type": "inject",
        "z": "cbf7f47c.caccc8",
        "name": "set spot1 offset",
        "props": [
            {
                "p": "device",
                "v": "spot1",
                "vt": "str"
            },
            {
                "p": "offset",
                "v": "0",
                "vt": "num"
            },
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "true",
        "payloadType": "bool",
        "x": 2940,
        "y": 4080,
        "wires": [
            [
                "1cde18b38154698e"
            ]
        ]
    },
    {
        "id": "224767e32291753f",
        "type": "debug",
        "z": "cbf7f47c.caccc8",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 3470,
        "y": 4100,
        "wires": []
    }
]

Why does that happen?
NR: v2.1.6

This is a reference, one of the pitfalls of javascript.

try it like

let spotlights = RED.util.cloneMessage(flow.get("spotlights")) ?? ...

No, that does not store the offset value at all, in the flow.spotlight.

In english it will be:
let the thingy be even an object or if it is null or undefined then the function which stores that kind of object.

Break it apart and do required steps when needed:

  1. Ask for object
  2. If not found, create with initial values
  3. modify if needed
  4. store back after modifications
  5. use as part of outgoing message.

like this?

let init = {
    "dimm": { "value": 0, "channel": 1 },
    "nc1": { "value": 0, "channel": 2 },
    "nc2": { "value": 255, "channel": 3 },
    "nc3": { "value": 0, "channel": 4 },
    "nc4": { "value": 0, "channel": 5 },
    "temp": { "value": 0, "channel": 6 },
    "zoom": { "value": 0, "channel": 7 },
    "offset": 0
}
let spotlights = flow.get("spotlights");
if (typeof spotlights === 'undefined'){
    flow.set("spotlights", {
        "spot1" : init,
        "spot2" : init
    });
    spotlights = flow.get("spotlights");
};
spotlights[msg.device]["offset"] = msg.offset;
msg.payload = spotlights[msg.device];
return msg;

This has the exact same behaviour.
It overwrites both instances of spotlight (spot1 and spot2) at the same time.

and spotlights[msg.device].offset = msg.offset;

does the same as well.

The problem you are seeing is because javascripts accesses objects by reference. The result is that spot1 and spot2 both point to the same init object. The result is that when you fetch that back from context and then modify, for example, spot1, you are actually modifying the original init object and so spot2 also changes. Try this

    flow.set("spotlights", {
        "spot1" : init,
        "spot2" : RED.util.cloneMessage(init)
    });

That creates a clone of init to be referenced by spot2

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.