Search code examples
jsonata

JSONATA: Creating Array from Nested Array of Objects


I'm new to JSONATA, so this is probably a pretty easy formatting problem that I don't know yet. I need to take a large object and reduce it down into something more manageable. I've been able to do this with objects, but am running into a problem with a particular data structure. It's metadata from a list of images, where the keywords are in an array of objects each with the structure {"name": "keyword"}. Like this:

{
    "body": {
    "files": [
      {
        "id": 101936854,
        "title": "Taco Salad",
        "keywords": [
          {
            "name": "background"
          },
          {
            "name": "baked"
          },
          {
            "name": "beef"
          }
        ]
      },
      {
        "id": 412961542,
        "title": "Fiji",
        "keywords": [
          {
            "name": "beach"
          },
          {
            "name": "sea"
          },
          {
            "name": "tree"
          }
        ]
      }
    ]
  }
}

When I use the query

$zip(body.files.id,body.files.title,body.files.[keywords.name])
{
    "id": $[0],
    "title": $[1],
    "keywords": $[2]
}

I get what I want, but only the first object, like so:

{
  "id": 101936854,
  "title": "Taco Salad",
  "keywords": [
    "background",
    "baked",
    "beef"
  ]
}

If I add the . to output all the objects, I get back multiple objects but only the first value of name in the array. Like so:

$zip(body.files.id,body.files.title,body.files.[keywords.name]).
{
    "id": $[0],
    "title": $[1],
    "keywords": $[2]
}

Gets:

[
  {
    "id": 101936854,
    "title": "Taco Salad",
    "keywords": "background"
  },
  {
    "id": 412961542,
    "title": "Fiji",
    "keywords": "beach"
  }
]

I believe this is because the input objects in that array all have the same key of name. So, I need to somehow get all the values of name and put them in one array called keywords.


Solution

  • If I'm not mistaken, you want something like this:

    body.files.{
      "id": id,
      "title": title,
      "keywords": keywords.name
    }
    

    or, you can play with the transform operator:

    (body ~> |files|{ "keywords": keywords.name }|).files
    

    which produces this output for your example:

    [
      {
        "id": 101936854,
        "title": "Taco Salad",
        "keywords": [
          "background",
          "baked",
          "beef"
        ]
      },
      {
        "id": 412961542,
        "title": "Fiji",
        "keywords": [
          "beach",
          "sea",
          "tree"
        ]
      }
    ]
    

    See it live here https://stedi.link/ybzLADi and here https://stedi.link/TLk7Loq

    P.S. if you do want to use $zip for it, I think you'd have to avoid arrays of arrays to prevent JSONata from flattening results, one possible implementation:

    $zip(
      body.files.id, 
      body.files.title, 
      body.files.{"keywords": keywords.name}
    ).{
      "id": $[0], "title": $[1], "keywords": $[2].keywords
    }