Filter data based on quantities and types

Hello everyone!

I’m having a really hard time trying to figure out this and would like to see if anyone can help me achieve it.

I have 7 items that are packs of 5, 10 and 20 and I also have these items as individual items. For example: “Digital picture” (1) and “Pack of 5 digital pictures” (pack of 5). As you can see on the json all these items have quantities (count).

I would like to filter these results as follows: If there are 1 or 2+ packs of “5 Digital pictures”, calc and remove 5 or 10 (based on the quantity of the pack) of the individual items called “Digital picture” for example and if it remains at least 1 Digital picture keep the item, if it equals “0” remove the item and just keep the pack.

All I would like to keep all the other items that are not part of the pack and have different names.

As I have 7 different packs with different names and quantities I need to do it on any of these in case it shows up on any request.

This is the data I receive.

[
{
"item": "Digital picture",
"count": 6
},
{
"item": "Prints - 8 x 10",
"count": 6
},
{
"item": "Prints - 5 x 7",
"count": 5
},
{
"item": "Photo album - 8 x 10",
"count": 1
},
{
"item": "Prints - 20 x 30",
"count": 1
},
{
"item": "Prints - 4 x 6",
"count": 1
},
{
"item": "All digital pictures",
"count": 1
},
{
"item": "EXPRESS Delivery (2 days)",
"count": 1
},
{
"item": "Pack of 5 digital pictures",
"count": 1
},
{
"item": "Pack of 5 prints 8"x10"",
"count": 1
},
{
"item": "Pack of 5 prints 5"x7"",
"count": 2
}
]

I did some testing but it’s not working for me.

{
  "nodes": [
    {
      "parameters": {
        "functionCode": "return [\n{\n\"item\": \"Digital picture\",\n\"count\": 6\n},\n{\n\"item\": \"Prints - 8 x 10\",\n\"count\": 6\n},\n{\n\"item\": \"Prints - 5 x 7\",\n\"count\": 5\n},\n{\n\"item\": \"Photo album - 8 x 10\",\n\"count\": 1\n},\n{\n\"item\": \"Prints - 20 x 30\",\n\"count\": 1\n},\n{\n\"item\": \"Prints - 4 x 6\",\n\"count\": 1\n},\n{\n\"item\": \"All digital pictures\",\n\"count\": 1\n},\n{\n\"item\": \"EXPRESS Delivery (2 days)\",\n\"count\": 1\n},\n{\n\"item\": \"Pack of 5 digital pictures\",\n\"count\": 1\n},\n{\n\"item\": \"Pack of 5 prints 8\"x10\"\",\n\"count\": 1\n},\n{\n\"item\": \"Pack of 5 prints 5\"x7\"\",\n\"count\": 2\n}\n]\n"
      },
      "name": "Sample data",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1800,
        1260
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "=Pack of 5 digital pictures",
              "operation": "contains",
              "value2": "Pack of 5 digital pictures"
            },
            {
              "value1": "Pack of 10 digital pictures",
              "value2": "Pack of 10 digital pictures"
            },
            {
              "value1": "Pack of 20 digital pictures",
              "value2": "Pack of 20 digital pictures"
            }
          ]
        },
        "combineOperation": "any"
      },
      "name": "Has a pack?1",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1950,
        1260
      ]
    },
    {
      "parameters": {
        "values": {
          "string": [
            {
              "name": "restantes",
              "value": "={{$node[\"Sample data\"].json[\"item\"].match(/[0-9]+/) * $node[\"Sample data\"].json[\"count\"] - $node[\"Sample data\"].json[\"count\"]}}"
            }
          ]
        },
        "options": {}
      },
      "name": "Minus1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        2150,
        1240
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$node[\"Has a pack?\"].json[\"item\"]}}",
              "operation": "contains",
              "value2": "digital"
            }
          ]
        }
      },
      "name": "Digital o Prints1",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        2300,
        1240
      ]
    }
  ],
  "connections": {
    "Sample data": {
      "main": [
        [
          {
            "node": "Has a pack?1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has a pack?1": {
      "main": [
        [
          {
            "node": "Minus1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Minus1": {
      "main": [
        [
          {
            "node": "Digital o Prints1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Hi @zerog, the workflow you have provided would be somewhat tricky as the IF node would run once for each item it receives (or only the first item when the Execute Once setting is enabled).

However, it seems your example items would represent all items in a single order. So we would need to query whether any of the items is Pack of 5 digital pictures, Pack of 5 prints 8"x10", or Pack of 5 prints 5"x7" and has a count > 0.

In the next step we then reduce the count value of each corresponding individual item by 5 for each pack present. If the count of the individual items is 0 (or less), remove the item completely.

Tbh, I can’t think of an elegant way of achieving this with the built-in n8n nodes. So I think this might be a case for custom JavaScript code manipulating the data in the way you want. I have put together a quick commented example snippet you can use in a Function node:

// Individual items and the corresponding pack items along with the count
const mappings = [{
  single_item: 'Digital picture',
  pack_item: 'Pack of 5 digital pictures',
  count_per_pack: 5
}, {
  single_item: 'Prints - 8 x 10',
  pack_item: 'Pack of 5 prints 8"x10"',
  count_per_pack: 5
}, {
  single_item: 'Prints - 5 x 7',
  pack_item: 'Pack of 5 prints 5"x7"',
  count_per_pack: 5
}];

// Go through all known mappings
for (mapping of mappings) {
  // Search for a matching pack item
  const pack_found = items.find(item => item.json.item == mapping.pack_item);
  // Search for a matching single item
  const item_found = items.find(item => item.json.item == mapping.single_item);
  // If both pack & single item are found
  if (pack_found && item_found) {
    // For each pack found, reduce the number of individual items by count_per_pack
    for (let i = 0; i < pack_found.json.count; i++) {
      item_found.json.count = item_found.json.count - mapping.count_per_pack;
    } 
  }
}

// Only return items with a count above 0
return items.filter(item => item.json.count > 0);

This snippet uses the following JS methods to apply its logic:

  • .find(): Pretty much what it says - finds an element matching the defined condition
  • .filter(): This only returns elements matching the defined condition
  • for and for…in: Looping constructs

This can easily be adjusted by adding new single items/packs as needed to the top mappings array.

image

Full Example Workflow
{
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "functionCode": "const example = [\n{\n\"item\": \"Digital picture\",\n\"count\": 6\n},\n{\n\"item\": \"Prints - 8 x 10\",\n\"count\": 6\n},\n{\n\"item\": \"Prints - 5 x 7\",\n\"count\": 5\n},\n{\n\"item\": \"Photo album - 8 x 10\",\n\"count\": 1\n},\n{\n\"item\": \"Prints - 20 x 30\",\n\"count\": 1\n},\n{\n\"item\": \"Prints - 4 x 6\",\n\"count\": 1\n},\n{\n\"item\": \"All digital pictures\",\n\"count\": 1\n},\n{\n\"item\": \"EXPRESS Delivery (2 days)\",\n\"count\": 1\n},\n{\n\"item\": \"Pack of 5 digital pictures\",\n\"count\": 1\n},\n{\n\"item\": \"Pack of 5 prints 8\\\"x10\\\"\",\n\"count\": 1\n},\n{\n\"item\": \"Pack of 5 prints 5\\\"x7\\\"\",\n\"count\": 2\n}\n]\n\nreturn example.map(e => {return {json: e}})"
      },
      "name": "Sample Data",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "functionCode": "// Individual items and the corresponding pack items along with the count\nconst mappings = [{\n  single_item: 'Digital picture',\n  pack_item: 'Pack of 5 digital pictures',\n  count_per_pack: 5\n}, {\n  single_item: 'Prints - 8 x 10',\n  pack_item: 'Pack of 5 prints 8\"x10\"',\n  count_per_pack: 5\n}, {\n  single_item: 'Prints - 5 x 7',\n  pack_item: 'Pack of 5 prints 5\"x7\"',\n  count_per_pack: 5\n}];\n\n// Go through all known mappings\nfor (mapping of mappings) {\n  // Search for a matching pack item\n  const pack_found = items.find(item => item.json.item == mapping.pack_item);\n  // Search for a matching single item\n  const item_found = items.find(item => item.json.item == mapping.single_item);\n  // If both pack & single item are found\n  if (pack_found && item_found) {\n    // For each pack found, reduce the number of individual items by count_per_pack\n    for (let i = 0; i < pack_found.json.count; i++) {\n      item_found.json.count = item_found.json.count - mapping.count_per_pack;\n    } \n  }\n}\n\n// Only return items with a count above 0\nreturn items.filter(item => item.json.count > 0);"
      },
      "name": "Update Items",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        680,
        300
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Sample Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sample Data": {
      "main": [
        [
          {
            "node": "Update Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

I hope this helps getting you started! Let me know if you have any trouble with this :slight_smile:

1 Like

This is absolutely amazing @MutedJam It worked exactly as I needed. Thank you so much for doing such an effort.