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!

1 Like

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.

3 Likes

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

1 Like

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

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

This kind of thing makes my head hurt :wink:

1 Like

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:

2 Likes