Filter Multi Dimensional Array

I can't seem to get the syntax right to only return the array with a "Value" containing "Gaming" in a multi dimensional array.
This is what i've got so far:

msg.payload = msg.payload.Volumes.Tags.filter(el => el.Value.includes("Gaming"))

My payload returns three "Volumes" and I want to filter out one of the "Volumes"

{
   "Volumes":[
      {
         "Attachments":[
            {
               "AttachTime":"2021-12-04T18:03:14.000Z",
               "Device":"xvdf",
               "InstanceId":"abcd",
               "State":"attached",
               "VolumeId":"abcd",
               "DeleteOnTermination":false
            }
         ],
         "AvailabilityZone":"ca-central-1a",
         "CreateTime":"2021-12-04T18:03:00.651Z",
         "Encrypted":false,
         "Size":125,
         "SnapshotId":"abcd",
         "State":"in-use",
         "VolumeId":"abcd",
         "Tags":[
            
         ],
         "VolumeType":"sc1",
         "MultiAttachEnabled":false
      },
      {
         "Attachments":[
            {
               "AttachTime":"2022-01-09T01:22:44.000Z",
               "Device":"/dev/sda1",
               "InstanceId":"abcd",
               "State":"attached",
               "VolumeId":"abcd",
               "DeleteOnTermination":false
            }
         ],
         "AvailabilityZone":"ca-central-1a",
         "CreateTime":"2022-01-09T01:22:28.846Z",
         "Encrypted":false,
         "Size":30,
         "SnapshotId":"",
         "State":"in-use",
         "VolumeId":"abcd",
         "Iops":100,
         "Tags":[
            {
               "Key":"Volume",
               "Value":"GamingVolume"
            }
         ],
         "VolumeType":"gp2",
         "MultiAttachEnabled":false
      },
      {
         "Attachments":[
            
         ],
         "AvailabilityZone":"ca-central-1a",
         "CreateTime":"2022-01-09T03:15:01.774Z",
         "Encrypted":false,
         "Size":30,
         "SnapshotId":"",
         "State":"available",
         "VolumeId":"abcd",
         "Iops":100,
         "Tags":[
            {
               "Key":"Snapshot",
               "Value":"Snapshot"
            }
         ],
         "VolumeType":"gp2",
         "MultiAttachEnabled":false
      }
   ]
}

hello .. you need to loop through Volumes and then through its individual Tags
and push the volume match to a temporary array (result),
or at least that how my thought process worked it out :wink:

let result = [];

msg.payload.Volumes.forEach(volume => {
    volume.Tags.forEach(tag => {
        if (tag.Value.startsWith("Gaming")) {
            result.push(volume)
        }
    })
})

msg.payload = result;

return msg;

Test Flow:

[{"id":"b558a74069d392f3","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{    \"Volumes\":[       {          \"Attachments\":[             {                \"AttachTime\":\"2021-12-04T18:03:14.000Z\",                \"Device\":\"xvdf\",                \"InstanceId\":\"abcd\",                \"State\":\"attached\",                \"VolumeId\":\"abcd\",                \"DeleteOnTermination\":false             }          ],          \"AvailabilityZone\":\"ca-central-1a\",          \"CreateTime\":\"2021-12-04T18:03:00.651Z\",          \"Encrypted\":false,          \"Size\":125,          \"SnapshotId\":\"abcd\",          \"State\":\"in-use\",          \"VolumeId\":\"abcd\",          \"Tags\":[                       ],          \"VolumeType\":\"sc1\",          \"MultiAttachEnabled\":false       },       {          \"Attachments\":[             {                \"AttachTime\":\"2022-01-09T01:22:44.000Z\",                \"Device\":\"/dev/sda1\",                \"InstanceId\":\"abcd\",                \"State\":\"attached\",                \"VolumeId\":\"abcd\",                \"DeleteOnTermination\":false             }          ],          \"AvailabilityZone\":\"ca-central-1a\",          \"CreateTime\":\"2022-01-09T01:22:28.846Z\",          \"Encrypted\":false,          \"Size\":30,          \"SnapshotId\":\"\",          \"State\":\"in-use\",          \"VolumeId\":\"abcd\",          \"Iops\":100,          \"Tags\":[             {                \"Key\":\"Volume\",                \"Value\":\"GamingVolume\"             }          ],          \"VolumeType\":\"gp2\",          \"MultiAttachEnabled\":false       },       {          \"Attachments\":[                       ],          \"AvailabilityZone\":\"ca-central-1a\",          \"CreateTime\":\"2022-01-09T03:15:01.774Z\",          \"Encrypted\":false,          \"Size\":30,          \"SnapshotId\":\"\",          \"State\":\"available\",          \"VolumeId\":\"abcd\",          \"Iops\":100,          \"Tags\":[             {                \"Key\":\"Snapshot\",                \"Value\":\"Snapshot\"             }          ],          \"VolumeType\":\"gp2\",          \"MultiAttachEnabled\":false       }    ] }","payloadType":"json","x":360,"y":840,"wires":[["2f027bc423a030ee","ad0d28f5265f8459"]]},{"id":"2f027bc423a030ee","type":"debug","z":"54efb553244c241f","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":480,"y":780,"wires":[]},{"id":"ad0d28f5265f8459","type":"function","z":"54efb553244c241f","name":"","func":"let result = [];\n\nmsg.payload.Volumes.forEach(volume => {\n    volume.Tags.forEach(tag => {\n        if (tag.Value.startsWith(\"Gaming\")) {\n            result.push(volume)\n        }\n    })\n})\n\nmsg.payload = result;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":840,"wires":[["b44fcbe216010fe7"]]},{"id":"b44fcbe216010fe7","type":"debug","z":"54efb553244c241f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":700,"y":840,"wires":[]}]

ps. by the way .. when you say .. containing "Gaming" .. the keyword is always at the start ? or could it be anywhere in the string of Value. if yes .. then possibly you'll need to change startsWith with RegEx or something.

Thank you very much! I can set the tag values so I can make sure it always starts the same.

There could be a problem with the above code.

If you have a Volume that has two or more Tags that have a "Gaming" keyword, you may end up with duplicates of the same Volume in the result.

If this is not what you want then we need to work on the code a bit more.

Understood. I can create unique tags and update the code as needed.
Thanks again!

The reason that fails is that msg.payload.Volumes.Tags is not an array. In terms of the syntax it should have been something like
msg.payload = msg.payload.Volumes.filter(el => el.Tags.includes("Gaming"))

@Colin I think includes is not going to find a match in this case because his Tags is an array of objects

"Tags":[
            {
               "Key":"Volume",
               "Value":"GamingVolume"
            }
         ],

if it was an array of strings eg "Tags":["Gaming","Sports"] it would ..
dont know if he can change the way his data is structured

This should work

msg.payload.Volumes = msg.payload.Volumes.filter(({Tags}) => {
    return Tags.find(obj => obj.Value.startsWith("Gaming"))
    })

return msg;

[edit] and should handle multiple tags.

Yes, I was just pointing out the problem with the call of filter().

Another option is to use a change node with this JSONata expression:

payload.Volumes[Tags[Value.$match(/gaming/i)]]

This kind of thing makes my head hurt :wink:

That's very nice. The volumes are also an array so it works with:

payload.[Volumes[Tags[Value.$match(/gaming/i)]]]

:joy: Yeah, it does help if you are "wired" a bit differently than the norm...

... which may be why I also love working with regex and xslt. :wink: