Search code examples
jsonjsonnet

Jsonnet object comprehension in array


I know how to do Array comprehension in Jsonnet like this, with the for loop the one object in the array gets looped with all my defined values:

local values = [
  'foo',
  'bar',
  'foobar',
];
{
list: 'movies', 
items: [
  {
      title: 'title',
      language: 'english',
      metadata: {
        author: 'god',
        value: v,
      },
    },
    for v in values
  ],
}

output:

{
   "items": [
      {
         "language": "english",
         "metadata": {
            "author": "god",
            "value": "foo"
         },
         "title": "title"
      },
      {
         "language": "english",
         "metadata": {
            "author": "god",
            "value": "bar"
         },
         "title": "title"
      },
      {
         "language": "english",
         "metadata": {
            "author": "god",
            "value": "foobar"
         },
         "title": "title"
      }
   ],
   "list": "movies"
}

I now want to do this with multiple objects in an array like this:

local values = [
  'foor',
  'bar',
  'foorbar',
];
{
list: 'movies', 
items: [
  {
      title: 'title',
      language: 'english',
      metadata: {
        author: 'god',
        value: v,
      },
    },
    {
      title: 'title2',
      language: 'german',
      metadata: {
        author: 'devil',
        value: v,
      },
    },
    {
      title: 'title3',
      language: 'spanish',
      metadata: {
        author: 'chutulu',
        value: v,
      },
    },
  ],
}

What to I need to do to have the following output? So every of the three objects get the three values

{
   "items": [
      {
         "language": "english",
         "metadata": {
            "author": "god",
            "value": "foor"
         },
         "title": "title1"
      },
      {
         "language": "english",
         "metadata": {
            "author": "god",
            "value": "bar"
         },
         "title": "title1"
      },
      {
         "language": "english",
         "metadata": {
            "author": "god",
            "value": "foorbar"
         },
         "title": "title1"
      }
      {
         "language": "german",
         "metadata": {
            "author": "devil",
            "value": "foor"
         },
         "title": "title2"
      },
      {
         "language": "german",
         "metadata": {
            "author": "devil",
            "value": "bar"
         },
         "title": "title2"
      },
      {
         "language": "german",
         "metadata": {
            "author": "devil",
            "value": "foorbar"
         },
         "title": "title2"
      }
      {
         "language": "spanish",
         "metadata": {
            "author": "chutulu",
            "value": "foor"
         },
         "title": "title3"
      },
      {
         "language": "spanish",
         "metadata": {
            "author": "chutulu",
            "value": "bar"
         },
         "title": "title3"
      },
      {
         "language": "spanish",
         "metadata": {
            "author": "chutulu",
            "value": "foorbar"
         },
         "title": "title3"
      }
   ],
   "list": "movies"
}

Solution

  • Pasting below two possible solutions, with their own tradeoffs (depending on how further you need to customize the final items array):

    1) Mixing arrays, with value "placement" in the comprehension

    local values = [
      'foor',
      'bar',
      'foorbar',
    ];
    local movies = [
      {
        title: 'title',
        language: 'english',
        metadata: {
          author: 'god',
        },
      },
      {
        title: 'title2',
        language: 'german',
        metadata: {
          author: 'devil',
        },
      },
      {
        title: 'title3',
        language: 'spanish',
        metadata: {
          author: 'chutulu',
        },
      },
    ];
    
    // Combine the `values` and `movies` arrays into `items`,
    // overloading movie's metadata.value with each value,
    // note the `metadata+:` syntax.
    {
      list: 'movies',
      items: [
        movie { metadata+: { value: value } }
        for movie in movies
        for value in values
      ],
    }
    

    2) Create movies(v) function that outputs the 3-lang array, aggregate over values and flatten it

    local values = [
      'foor',
      'bar',
      'foorbar',
    ];
    local movies(v) = [
      {
        title: 'title',
        language: 'english',
        metadata: {
          author: 'god',
          value: v,
        },
      },
      {
        title: 'title2',
        language: 'german',
        metadata: {
          author: 'devil',
          value: v,
        },
      },
      {
        title: 'title3',
        language: 'spanish',
        metadata: {
          author: 'chutulu',
          value: v,
        },
      },
    ];
    
    // To build the final `items` array we need to flatten
    // the array-of-arrays created by the comprehension, as
    // each element is indeed a 3-movies array
    {
      list: 'movies',
      items: std.flattenArrays([
        movies(v)
        for v in values
      ]),
    }