skip to Main Content

I’m trying to generate a new json from an existing one. In the new json is an array with one entry, which needs to remain the first entry in the array. With jq generate additional entries to this arrays. But my code replaces the existing entry. I’ve created a smaller representation of my Problem.

This following code is what I have so far.

echo '{"Results": [{"Input": [{"foo":" bar", "this": "that"}, {"foo": "bing"}]}]}' | 
  jq '{"sections": [{"first": "element", "that": "is needed at pos 0 in this array"},  .Results[] | .Input[]? | {"markdown": true, "facts":[{name: "MyFoo", value: .foo}]}]}'

This is the result i, which is missing the first element of the sections list.

{
  "sections": [
    {
      "markdown": true,
      "facts": [
        {
          "name": "MyFoo",
          "value": " bar"
        }
      ]
    },
    {
      "markdown": true,
      "facts": [
        {
          "name": "MyFoo",
          "value": "bing"
        }
      ]
    }
  ]
}

My desired result would be the following:

{
  "sections": [
    {
      "first": "element",
      "that": "is needed at pos 0 in this array"
    },
    {
      "markdown": true,
      "facts": [
        {
          "name": "MyFoo",
          "value": " bar"
        }
      ]
    },
    {
      "markdown": true,
      "facts": [
        {
          "name": "MyFoo",
          "value": "bing"
        }
      ]
    }
  ]
}

I’v tried to use two linked jq command but his destroys the order in the sections list.

echo '{"Results": [{"Input": [{"foo":" bar", "this": "that"}, {"foo": "bing"}]}]}' | 
  jq '{"sections": [ .Results[] | .Input[]? | {"markdown": true, "facts":[{name: "MyFoo", value: .foo}]}]}' |
  jq '.sections += [{"first": "element", "that": "is needed at pos 0 in this array"}]'

2

Answers


  1. Something like this?

    $ echo '{"Results": [{"Input": [{"foo":" bar", "this": "that"}, {"foo": "bing"}]}]}' | 
      jq '{sections: ([{first: "element", that: "is needed at pos 0 in this array"}] + [.Results[].Input[] | { markdown: true, facts: [ { name: "MyFoo", value: .foo }]}])}'
    {
      "sections": [
        {
          "first": "element",
          "that": "is needed at pos 0 in this array"
        },
        {
          "markdown": true,
          "facts": [
            {
              "name": "MyFoo",
              "value": " bar"
            }
          ]
        },
        {
          "markdown": true,
          "facts": [
            {
              "name": "MyFoo",
              "value": "bing"
            }
          ]
        }
      ]
    }
    

    In particular, note the use of + to concatenate two arrays.

    Login or Signup to reply.
  2. You are almost there.
    Let’s format your attempt a bit, to understand what it does:

    {
      "sections": [
        {"first": "element", "that": "is needed at pos 0 in this array"},
        .Results[] | .Input[]? | {"markdown": true, "facts": [{name: "MyFoo", value: .foo}]}
      ]
    }
    

    I formatted it according to your intent: you want an object whose sections property is an array that has the object { "first": "element" ... } as its first item and the other items are computed from the properties .Results.Input of the input object.

    Unfortunately, the documentation of jq does not specify the precedence of its operators. While everybody (including me) assumes that the pipe operator (|) has higher precedence than the comma operator (,) (an assumption probably based on how these operators work in the languages inspired from C), in jq is the other way around: , has higher precedence than |.

    This makes your code work equivalent to this:

    {
      "sections": [
        ({"first": "element", "that": "is needed at pos 0 in this array"}, .Results[])
        | .Input[]? | {"markdown": true, "facts": [{name: "MyFoo", value: .foo}]}
      ]
    }
    

    The first filter ({"first": ... }, .Results[]) produces:

    {
      "sections":[
        {"first": "element","that":"is needed at pos 0 in this array"},
        {"Input": [{"foo": " bar","this": "that"}, {"foo": "bing"}]}
      ]
    }
    

    The next filter, .Input[] discards the object {first: "element" ...} and its output is [{"foo": " bar","this": "that"}, {"foo": "bing"}] and now it is clear why the output of the entire program is not the one that you expect.

    The solution is simple: wrap the pipeline that processes the input in parentheses and it will work.


    Given that jq language is not JSON (but it includes JSON), you don’t have to wrap the keys in quotes. Getting rid of the quotes around keys and wrapping the program on multiple lines makes it easier to read:

    {
      sections: [
        { first: "element", that: "is needed at pos 0 in this array" },
        (.Results[] | .Input[]? | { markdown: true, facts: [{ name: "MyFoo", value: .foo }]})
      ]
    }
    

    Check it online on the jq playground.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search