# Feasibility of implementing functional operators without javascript

**URL:** https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361
**Category:** General
**Tags:** function-node
**Created:** [8 May 2022 08:45 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361 "2022-05-08T08:45:44Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![cdaringe](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/cdaringe/32/61255_2.png) [@cdaringe](https://discourse.nodered.org/u/cdaringe)
#### Post date: [8 May 2022 08:45 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/1 "2022-05-08T08:45:44Z")

</div>

Hey all,

I want to be able to be able to do FP like operations using VPL primitives vs having to drop into JS.

Example.

Suppose I wanted to implement a visual version of reduce (or "fold"). Suppose I wanted to multiply the accumulator by each value in an array on each reduce iteration.

In a raw `function` `node`, I can do this quite easily:

```javascript
const { init, values } = msg.payload
return values.reduce((acc, it) => acc * it) , init)

```

Then if I sent a message of `{ init: 1, values: [2,2,2,3] }`, the `node` would emit `24`.

Neat. But what if I'm map/filter/reduce/flattening over a series of functions? It'd be nice to get that visualization surfaced into red, vs being buried in javascript.

Here's an implementation of a `reduce` function wired into a `multiplication` function (`*`):

[![](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/0/2/0239a8fe1f16bc6940caf58ff32f7bba09de483e.png) ](https://static.cdaringe.com/img/reduce-mult.png)

The idea here is that `reduce` could be implemented in such a way where iteration values could be piped thru user processing flows--such as `*`--then on completion, the final value could be emitted out of a finalized output (vs the iteration output). The above works, but is a little hacky 🙂. The node-red flow JSON is attached below in the post. The major bummer of this `reduce` design is that is _not_ generic. It is tightly coupled to the `*` function.

A generic impl would look something like:

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/7/4/74b95d48b346875bd460d143650d31bba6b2c3f1.png)

The above is known imperfect. The key intent is that the `*` function would no longer be coupled to the `reduce` implementation. Callers could use it like:

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/e/2/e2ee1c5c9ea9145f29ba0b124cc079e34c9c42e6.png)

In this case, I continue to use `reduce` with `*` for consistency. However, ideally `reduce` users would be able to import it pipe iteration values through any number of composable nodes. Less code, more visual. Yada yada.

What do you think?

> **reduce/fold**
>
> ```json
> [
> {
> "id": "e809845bab449b69",
> "type": "tab",
> "label": "reduce",
> "disabled": false,
> "info": "",
> "env": []
> },
> {
> "id": "a519499dfb7e0895",
> "type": "function",
> "z": "e809845bab449b69",
> "name": "reduce",
> "func": "/**\n * msg.input = [collection, init]\n */\n\nconst remaining = context.get('pending-processing')\n\nif (remaining) {\n node.log(JSON.stringify({ acc: msg.payload, remaining }))\n if (remaining.length) {\n const [hd, ...tl] = remaining\n context.set('pending-processing', tl)\n node.send({ __reduce__ : true, payload: [msg.payload, hd] })\n } else {\n context.set('pending-processing', null)\n node.send({ __final__ : true, payload: msg.payload })\n }\n} else {\n node.log(JSON.stringify(msg))\n const [collection, init] = msg.payload\n if (!collection.length) {\n node.send({ __final__ : true, payload: init })\n }\n const [hd, ...tl] = collection\n context.set('pending-processing', tl)\n node.send({ __reduce__ : true, payload: [init, hd] })\n}\n",
> "outputs": 1,
> "noerr": 0,
> "initialize": "context.set('pending-processing', null)",
> "finalize": "context.set('pending-processing', null)",
> "libs": [],
> "x": 310,
> "y": 180,
> "wires": [
> [
> "fa3c3aa82c4f52cc",
> "6b08965615c9c3da"
> ]
> ]
> },
> {
> "id": "fa3c3aa82c4f52cc",
> "type": "function",
> "z": "e809845bab449b69",
> "name": "emit-needs-reduction",
> "func": "if (msg && msg. __reduce__ ) {\n node.send(msg)\n}",
> "outputs": 1,
> "noerr": 0,
> "initialize": "",
> "finalize": "",
> "libs": [],
> "x": 520,
> "y": 120,
> "wires": [
> [
> "fc6c5ed7f664f5c3"
> ]
> ]
> },
> {
> "id": "6b08965615c9c3da",
> "type": "function",
> "z": "e809845bab449b69",
> "name": "emit-final-reduction",
> "func": "if (msg && msg. __final__ ) {\n node.send({ payload: msg.payload })\n}",
> "outputs": 1,
> "noerr": 0,
> "initialize": "",
> "finalize": "",
> "libs": [],
> "x": 510,
> "y": 240,
> "wires": [
> [
> "fa0ecd27d0532086"
> ]
> ]
> },
> {
> "id": "fa0ecd27d0532086",
> "type": "debug",
> "z": "e809845bab449b69",
> "name": "out",
> "active": true,
> "tosidebar": true,
> "console": false,
> "tostatus": false,
> "complete": "true",
> "targetType": "full",
> "statusVal": "",
> "statusType": "auto",
> "x": 710,
> "y": 240,
> "wires": []
> },
> {
> "id": "fc6c5ed7f664f5c3",
> "type": "function",
> "z": "e809845bab449b69",
> "name": "acc ^ x",
> "func": "// node.log(JSON.stringify(msg))\nconst [acc, curr] = msg.payload\nreturn { payload: acc * curr };",
> "outputs": 1,
> "noerr": 0,
> "initialize": "",
> "finalize": "",
> "libs": [],
> "x": 720,
> "y": 120,
> "wires": [
> [
> "a519499dfb7e0895"
> ]
> ]
> },
> {
> "id": "0c4bd75423f8ce53",
> "type": "inject",
> "z": "e809845bab449b69",
> "name": "init: fold-raise",
> "props": [
> {
> "p": "payload"
> }
> ],
> "repeat": "",
> "crontab": "",
> "once": false,
> "onceDelay": 0.1,
> "topic": "",
> "payload": "[[2,2,2,2,2,2,2,2,3], 1]",
> "payloadType": "json",
> "x": 90,
> "y": 180,
> "wires": [
> [
> "a519499dfb7e0895"
> ]
> ]
> }
> ]
> 
> ```

---

<div class="post-metadata">

### Author: ![cymplecy](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/cymplecy/32/2773_2.png) [@cymplecy](https://discourse.nodered.org/u/cymplecy)
#### Post date: [8 May 2022 09:04 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/2 "2022-05-08T09:04:13Z")

</div>

My first impression is that Node-RED is the wrong tool for this job 🙂

I would say Snap! is the VPL vehicle for this sort of computer science, as it already has most constructs of this sort built-in or easily accessible by importing a library

It might be interesting (to a few people) to try to do it but it'd really be just doing it for the sake of doing it 🙂

---

<div class="post-metadata">

### Author: ![cdaringe](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/cdaringe/32/61255_2.png) [@cdaringe](https://discourse.nodered.org/u/cdaringe)
#### Post date: [8 May 2022 09:16 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/3 "2022-05-08T09:16:54Z")

</div>

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/9/9/99243db12c95c28dbc553f1c2c429a32cf5e7979.png)

subflows 🤦‍♂️

Super works 🙂

---

<div class="post-metadata">

### Author: ![cdaringe](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/cdaringe/32/61255_2.png) [@cdaringe](https://discourse.nodered.org/u/cdaringe)
#### Post date: [8 May 2022 09:18 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/4 "2022-05-08T09:18:09Z")

</div>

> [@cymplecy](#):
>
> really be just doing it for the sake of doing it

That's where I'm at. I'm not actually shipping anything on this, I just want to do some feasibility assessments.

Thx for the recc

---

<div class="post-metadata">

### Author: ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)
#### Post date: [8 May 2022 09:20 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/5 "2022-05-08T09:20:06Z")

</div>

Can you share this flow please? I'm just curious and might have some suggestions for you.

---

<div class="post-metadata">

### Author: ![cdaringe](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/cdaringe/32/61255_2.png) [@cdaringe](https://discourse.nodered.org/u/cdaringe)
#### Post date: [8 May 2022 09:26 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/6 "2022-05-08T09:26:31Z")

</div>

Sure thing:

```auto
[
    {
        "id": "e03926201dbef10b",
        "type": "subflow",
        "name": "reduce",
        "info": "",
        "category": "",
        "in": [
            {
                "x": 200,
                "y": 200,
                "wires": [
                    {
                        "id": "c703fa696129c8da"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 720,
                "y": 140,
                "wires": [
                    {
                        "id": "b56d2695f1458c94",
                        "port": 0
                    }
                ]
            },
            {
                "x": 720,
                "y": 260,
                "wires": [
                    {
                        "id": "f9050bab453bb9ae",
                        "port": 0
                    }
                ]
            }
        ],
        "env": [],
        "meta": {},
        "color": "#DDAA99"
    },
    {
        "id": "c703fa696129c8da",
        "type": "function",
        "z": "e03926201dbef10b",
        "name": "reduce",
        "func": "/**\n * msg.input = [collection, init]\n */\n\nconst remaining = context.get('pending-processing')\n\nif (remaining) {\n node.log(JSON.stringify({ acc: msg.payload, remaining }))\n if (remaining.length) {\n const [hd, ...tl] = remaining\n context.set('pending-processing', tl)\n node.send({ __reduce__ : true, payload: { acc: msg.payload.acc, curr: hd } })\n } else {\n context.set('pending-processing', null)\n node.send({ __final__ : true, payload: msg.payload.acc })\n }\n} else {\n node.log(JSON.stringify(msg))\n const [collection, init] = msg.payload\n if (!collection.length) {\n node.send({ __final__ : true, payload: init })\n }\n const [hd, ...tl] = collection\n context.set('pending-processing', tl)\n node.send({ __reduce__ : true, payload: { acc: init, curr: hd } })\n}\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "context.set('pending-processing', null)",
        "finalize": "context.set('pending-processing', null)",
        "libs": [],
        "x": 350,
        "y": 200,
        "wires": [
            [
                "b56d2695f1458c94",
                "f9050bab453bb9ae"
            ]
        ]
    },
    {
        "id": "b56d2695f1458c94",
        "type": "function",
        "z": "e03926201dbef10b",
        "name": "emit-needs-reduction",
        "func": "if (msg && msg. __reduce__ ) {\n node.send(msg)\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 560,
        "y": 140,
        "wires": [
            []
        ]
    },
    {
        "id": "f9050bab453bb9ae",
        "type": "function",
        "z": "e03926201dbef10b",
        "name": "emit-final-reduction",
        "func": "if (msg && msg. __final__ ) {\n node.send({ payload: msg.payload })\n}",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 550,
        "y": 260,
        "wires": [
            []
        ]
    },
    {
        "id": "3b9a9d02857dd4a7",
        "type": "tab",
        "label": "sum",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "45a4784b28a7e141",
        "type": "subflow:e03926201dbef10b",
        "z": "3b9a9d02857dd4a7",
        "name": "",
        "x": 370,
        "y": 260,
        "wires": [
            [
                "aa8f05cf3715a38f"
            ],
            [
                "54575abe13e508af"
            ]
        ]
    },
    {
        "id": "0a665e3f31bf6c9b",
        "type": "inject",
        "z": "3b9a9d02857dd4a7",
        "name": "[[1,2,3],1]",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[[1,2,3],1]",
        "payloadType": "json",
        "x": 180,
        "y": 260,
        "wires": [
            [
                "45a4784b28a7e141"
            ]
        ]
    },
    {
        "id": "aa8f05cf3715a38f",
        "type": "function",
        "z": "3b9a9d02857dd4a7",
        "name": "*",
        "func": "const { acc, curr } = msg.payload\nmsg.payload.acc = acc * curr\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 510,
        "y": 160,
        "wires": [
            [
                "25037cbf35327d0a"
            ]
        ]
    },
    {
        "id": "54575abe13e508af",
        "type": "debug",
        "z": "3b9a9d02857dd4a7",
        "name": "out",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 650,
        "y": 260,
        "wires": []
    },
    {
        "id": "25037cbf35327d0a",
        "type": "function",
        "z": "3b9a9d02857dd4a7",
        "name": "+ 1",
        "func": "const { acc } = msg.payload\nmsg.payload.acc = acc + 1\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 650,
        "y": 160,
        "wires": [
            [
                "45a4784b28a7e141"
            ]
        ]
    }
]

```

---

<div class="post-metadata">

### Author: ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)
#### Post date: [8 May 2022 11:03 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/7 "2022-05-08T11:03:54Z")

</div>

Hi @cdaringe

I thought you might be using context. While that in itself is not a problem you need to consider the async nature of node/node-red & what happens if messages travel in quick succession.

I touched up your flow and subflow to use purely the `msg` object & avoid context. NOTE: I also moved the "looping" inside of the subflow (but that was purely for demonstration purposes - your flow design was fine also).

One other point, as there is now no internal state, you could actually use `link call` nodes & have a regular Flow TAB with your "subroutines" on - I think this is more manageable than subflows.

Anyhow, enough waffle, here is my modifications & a demonstration of the problem using context...

![chrome_2v6BVdCULK](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/1/6/16b8b2a16422b11649c1fcda3242acb9f4a45d41.gif)

```auto
[{"id":"6e90e73e20b56586","type":"subflow","name":"reduce-mod","info":"","category":"","in":[{"x":120,"y":180,"wires":[{"id":"79a672384cc020eb"}]}],"out":[{"x":420,"y":180,"wires":[{"id":"79a672384cc020eb","port":1}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"79a672384cc020eb","type":"function","z":"6e90e73e20b56586","name":"reduce","func":"\n/**\n * msg.input = [collection, init]\n */\n\nif (!msg. __remaining__ ) {\n msg.original_payload = RED.util.cloneMessage(msg.payload)\n const [collection, init] = msg.payload\n if (!collection.length) {\n msg.payload = init\n delete msg.operations\n node.send([null, msg]) //final\n }\n const [hd, ...tl] = collection\n msg. __remaining__ = tl\n msg.payload = { acc: init, curr: hd }\n node.send([msg, null]) //reduce\n} else {\n if ( msg. __remaining__.length) {\n const [hd, ...tl] = msg. __remaining__ \n msg. __remaining__ = tl\n msg.payload = { acc: msg.payload.acc, curr: hd }\n node.send([msg, null]) //reduce\n } else {\n delete msg. __remaining__ \n delete msg.operations\n msg.payload = msg.payload.acc\n node.send([null, msg]) //final\n }\n}\n\n","outputs":2,"noerr":0,"initialize":"context.set('pending-processing', null)","finalize":"context.set('pending-processing', null)","libs":[],"x":270,"y":180,"wires":[["1b23e962c7016539"],[]]},{"id":"1b23e962c7016539","type":"function","z":"6e90e73e20b56586","name":"perform operations","func":"let ops = msg.operations || []\nfor (let index = 0; index < ops.length; index++) {\n const op = ops[index];\n op && op(msg)\n}\nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":290,"y":100,"wires":[["79a672384cc020eb"]]},{"id":"e03926201dbef10b","type":"subflow","name":"reduce","info":"","category":"","in":[{"x":200,"y":200,"wires":[{"id":"c703fa696129c8da"}]}],"out":[{"x":720,"y":140,"wires":[{"id":"b56d2695f1458c94","port":0}]},{"x":720,"y":260,"wires":[{"id":"f9050bab453bb9ae","port":0}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"c703fa696129c8da","type":"function","z":"e03926201dbef10b","name":"reduce","func":"/**\n * msg.input = [collection, init]\n */\n\nconst remaining = context.get('pending-processing')\n\nif (remaining) {\n node.log(JSON.stringify({ acc: msg.payload, remaining }))\n if (remaining.length) {\n const [hd, ...tl] = remaining\n context.set('pending-processing', tl)\n node.send({ __reduce__ : true, payload: { acc: msg.payload.acc, curr: hd } })\n } else {\n context.set('pending-processing', null)\n node.send({ __final__ : true, payload: msg.payload.acc })\n }\n} else {\n node.log(JSON.stringify(msg))\n const [collection, init] = msg.payload\n if (!collection.length) {\n node.send({ __final__ : true, payload: init })\n }\n const [hd, ...tl] = collection\n context.set('pending-processing', tl)\n node.send({ __reduce__ : true, payload: { acc: init, curr: hd } })\n}\n","outputs":1,"noerr":0,"initialize":"context.set('pending-processing', null)","finalize":"context.set('pending-processing', null)","libs":[],"x":350,"y":200,"wires":[["b56d2695f1458c94","f9050bab453bb9ae"]]},{"id":"b56d2695f1458c94","type":"function","z":"e03926201dbef10b","name":"emit-needs-reduction","func":"if (msg && msg. __reduce__ ) {\n node.send(msg)\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":140,"wires":[[]]},{"id":"f9050bab453bb9ae","type":"function","z":"e03926201dbef10b","name":"emit-final-reduction","func":"if (msg && msg. __final__ ) {\n node.send({ payload: msg.payload })\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":260,"wires":[[]]},{"id":"45a4784b28a7e141","type":"subflow:e03926201dbef10b","z":"3b9a9d02857dd4a7","name":"","x":250,"y":320,"wires":[["aa8f05cf3715a38f"],["54575abe13e508af"]]},{"id":"aa8f05cf3715a38f","type":"function","z":"3b9a9d02857dd4a7","name":"*","func":"const { acc, curr } = msg.payload\nmsg.payload.acc = acc * curr\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":290,"y":240,"wires":[["25037cbf35327d0a"]]},{"id":"54575abe13e508af","type":"debug","z":"3b9a9d02857dd4a7","name":"out","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":630,"y":320,"wires":[]},{"id":"25037cbf35327d0a","type":"function","z":"3b9a9d02857dd4a7","name":"+ 1","func":"const { acc } = msg.payload\nmsg.payload.acc = acc + 1\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":240,"wires":[["45a4784b28a7e141"]]},{"id":"6e9fbdfba22561d9","type":"debug","z":"3b9a9d02857dd4a7","name":"out-mod","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":640,"y":400,"wires":[]},{"id":"5c1f55fe66011405","type":"function","z":"3b9a9d02857dd4a7","name":"Operations","func":"msg.operations = [\n function op1(msg){\n const { acc, curr } = msg.payload\n msg.payload.acc = acc * curr\n return msg\n },\n function op2(msg){\n const { acc } = msg.payload\n msg.payload.acc = acc + 1\n return msg\n },\n]\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":270,"y":400,"wires":[["0897765c010121b0"]]},{"id":"0897765c010121b0","type":"subflow:6e90e73e20b56586","z":"3b9a9d02857dd4a7","name":"","x":450,"y":400,"wires":[["6e9fbdfba22561d9"]]},{"id":"e7aff0bd83b37c28","type":"link in","z":"3b9a9d02857dd4a7","name":"link in 1","links":["1b90fc3a4414c55d","37669cdbbb8ca3e4"],"x":135,"y":320,"wires":[["45a4784b28a7e141"]]},{"id":"8b739a88bd214e37","type":"link in","z":"3b9a9d02857dd4a7","name":"link in 2","links":["1b90fc3a4414c55d","37669cdbbb8ca3e4"],"x":135,"y":400,"wires":[["5c1f55fe66011405"]]},{"id":"3e826180a230c270","type":"inject","z":"3b9a9d02857dd4a7","name":"[[1,2,3],1]","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[[1,2,3],1]","payloadType":"json","x":200,"y":80,"wires":[["37669cdbbb8ca3e4"]]},{"id":"37669cdbbb8ca3e4","type":"link out","z":"3b9a9d02857dd4a7","name":"link out 3","mode":"link","links":["e7aff0bd83b37c28","8b739a88bd214e37"],"x":615,"y":80,"wires":[]},{"id":"1b8623323d558cfb","type":"inject","z":"3b9a9d02857dd4a7","name":"[[5,5],5]","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[[5,5],5]","payloadType":"json","x":190,"y":120,"wires":[["37669cdbbb8ca3e4"]]},{"id":"0c46c4820db096f6","type":"inject","z":"3b9a9d02857dd4a7","name":"fast data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":155,"y":160,"wires":[["fe5041e3ceeee977"]],"l":false},{"id":"fe5041e3ceeee977","type":"function","z":"3b9a9d02857dd4a7","name":"3 in a row [[1,2,3],1] [[5,5],5] [[1,2,3],100]","func":"node.send({payload: [[1,2,3],1]});\nnode.send({payload: [[5,5],5]});\nnode.send({payload: [[3,4,5],100]});\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":160,"wires":[["37669cdbbb8ca3e4"]]}]

```

Hope some of that is useful to you.

---

<div class="post-metadata">

### Author: ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)
#### Post date: [8 May 2022 11:19 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/8 "2022-05-08T11:19:50Z")

</div>

And here it is using LINK CALL...

![chrome_esXMvRoTGN](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/1/9/1999acc5bbb0e094523606fd5c2c46df60a252e8.gif)

```auto
[{"id":"02adb77ebb69e62d","type":"link call","z":"3b9a9d02857dd4a7","name":"","links":["299eee6d0c3972bd"],"linkType":"static","timeout":"30","x":530,"y":520,"wires":[["eeaf838d16bc8fb8"]]},{"id":"065cb43a50fd1a18","type":"function","z":"3b9a9d02857dd4a7","name":"Operations","func":"msg.operations = [\n function op1(msg){\n const { acc, curr } = msg.payload\n msg.payload.acc = acc * curr\n return msg\n },\n function op2(msg){\n const { acc } = msg.payload\n msg.payload.acc = acc + 1\n return msg\n },\n]\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":520,"wires":[["02adb77ebb69e62d"]]},{"id":"eeaf838d16bc8fb8","type":"debug","z":"3b9a9d02857dd4a7","name":"out-link-call","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":730,"y":520,"wires":[]},{"id":"3f21c0b5cdadf02f","type":"inject","z":"3b9a9d02857dd4a7","name":"[[1,2,3],1]","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[[1,2,3],1]","payloadType":"json","x":140,"y":500,"wires":[["065cb43a50fd1a18"]]},{"id":"68357e278106dd6a","type":"inject","z":"3b9a9d02857dd4a7","name":"[[5,5],5]","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[[5,5],5]","payloadType":"json","x":130,"y":540,"wires":[["065cb43a50fd1a18"]]},{"id":"91bb7b7ad4deab38","type":"group","z":"3b9a9d02857dd4a7","name":"Reducer","style":{"fill":"#ffffbf","label":true,"color":"#000000"},"nodes":["0523dd7cda780875","c47d901584d2aad5","299eee6d0c3972bd","55ba7aa9a6717650"],"x":284,"y":599,"w":352,"h":142},{"id":"0523dd7cda780875","type":"function","z":"3b9a9d02857dd4a7","g":"91bb7b7ad4deab38","name":"reduce","func":"\n/**\n * msg.input = [collection, init]\n */\n\nif (!msg. __remaining__ ) {\n msg.original_payload = RED.util.cloneMessage(msg.payload)\n const [collection, init] = msg.payload\n if (!collection.length) {\n msg.payload = init\n delete msg.operations\n updateStatus(msg.original_payload, msg.payload)\n node.send([null, msg]) //final\n }\n const [hd, ...tl] = collection\n msg. __remaining__ = tl\n msg.payload = { acc: init, curr: hd }\n node.send([msg, null]) //reduce\n} else {\n if ( msg. __remaining__.length) {\n const [hd, ...tl] = msg. __remaining__ \n msg. __remaining__ = tl\n msg.payload = { acc: msg.payload.acc, curr: hd }\n node.send([msg, null]) //reduce\n } else {\n delete msg. __remaining__ \n delete msg.operations\n msg.payload = msg.payload.acc\n updateStatus(msg.original_payload, msg.payload)\n node.send([null, msg]) //final\n }\n}\n\nfunction updateStatus(orig, result) {\n orig = JSON.stringify(orig, null, 0);\n node.status({fill:\"green\",shape:\"dot\",text:`${orig} => ${result}`});\n}","outputs":2,"noerr":0,"initialize":"context.set('pending-processing', null)","finalize":"context.set('pending-processing', null)","libs":[],"x":470,"y":700,"wires":[["c47d901584d2aad5"],["55ba7aa9a6717650"]]},{"id":"c47d901584d2aad5","type":"function","z":"3b9a9d02857dd4a7","g":"91bb7b7ad4deab38","name":"perform operations","func":"let ops = msg.operations || []\nfor (let index = 0; index < ops.length; index++) {\n const op = ops[index];\n op && op(msg)\n}\nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":640,"wires":[["0523dd7cda780875"]]},{"id":"299eee6d0c3972bd","type":"link in","z":"3b9a9d02857dd4a7","g":"91bb7b7ad4deab38","name":"reduce-subroutine","links":[],"x":325,"y":700,"wires":[["0523dd7cda780875"]]},{"id":"55ba7aa9a6717650","type":"link out","z":"3b9a9d02857dd4a7","g":"91bb7b7ad4deab38","name":"link out 4","mode":"return","links":[],"x":595,"y":700,"wires":[]}]

```

---

<div class="post-metadata">

### Author: ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)
#### Post date: [8 May 2022 13:41 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/9 "2022-05-08T13:41:53Z")

</div>

And, of course, if this was generally useful to people, you could easily convert that into a simple node.

---

<div class="post-metadata">

### Author: ![cdaringe](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/cdaringe/32/61255_2.png) [@cdaringe](https://discourse.nodered.org/u/cdaringe)
#### Post date: [9 May 2022 04:14 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/10 "2022-05-09T04:14:57Z")

</div>

Hey Steve, thx for sharing those flows. I appreciate you taking the time to spiff them up!

I have some additional thoughts and ideas I want to noodle through, but am having a difficult time expressing them at the moment. Perhaps I'll write 'em up, and get some feedback from ya later. Something something message purity, something something binding `.operations` reference as substitute for params input, something about iteration msgs flowing thru operations nodes vs programmatic operations execution, [something something complete](https://www.youtube.com/watch?v=0oGMbAIcXCQ). It's no doubt great stuff! But really, node-red has node input constraints that inhibit me from expressing my design in the visual way that I want to, I think. Not a problem, ...just wrestlin thru it 🙂

Much appreciated!

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)
#### Post date: [8 July 2022 04:15 UTC](https://discourse.nodered.org/t/feasibility-of-implementing-functional-operators-without-javascript/62361/11 "2022-07-08T04:15:01Z")

</div>

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