JQ returns json lines without commas

To be clear – I want a pseudo-JSON output that will be written into another static JSON file here… so not wrapped in an array or anything, simply getting the missing commas after each entity in my output.

Currently my query is:

.[] | select(.auditId == "categories") |
{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*(.auditProperty):* (.actual) (expected (.expected))"
  }
}

Which outputs:

{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*performance:* 1 (expected 0.8)"
  }
}
{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*accessibility:* 1 (expected 0.9)"
  }
}
{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*best-practices:* 0.96 (expected 0.9)"
  }
}
{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*seo:* 0.64 (expected 0.5)"
  }
}

When actually I want:

{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*performance:* 1 (expected 0.8)"
  }
},
{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*accessibility:* 1 (expected 0.9)"
  }
},
{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*best-practices:* 0.96 (expected 0.9)"
  }
},
{
  "type": "section",
  "text": {
    "type": "mrkdwn",
    "text": "*seo:* 0.64 (expected 0.5)"
  }
},

Note the comma after each entity! Its driving me nuts, I have tried adding a join(", ") in various places but that either does nothing to the final output or fails to compile depending on where I place it.

Here is the jqplay with data included https://jqplay.org/s/xx3F_IWn03g


Original input JSON:

[
  {
    "name": "minScore",
    "expected": 0.8,
    "actual": 1,
    "values": [
      1,
      1,
      1
    ],
    "operator": ">=",
    "passed": true,
    "auditProperty": "performance",
    "auditId": "categories",
    "level": "error",
    "url": "http://localhost:8080/page2"
  },
  {
    "name": "minScore",
    "expected": 0.9,
    "actual": 1,
    "values": [
      1,
      1,
      1
    ],
    "operator": ">=",
    "passed": true,
    "auditProperty": "accessibility",
    "auditId": "categories",
    "level": "error",
    "url": "http://localhost:8080/page2"
  },
  {
    "name": "minScore",
    "expected": 0.9,
    "actual": 0.96,
    "values": [
      0.93,
      0.96,
      0.96
    ],
    "operator": ">=",
    "passed": true,
    "auditProperty": "best-practices",
    "auditId": "categories",
    "level": "error",
    "url": "http://localhost:8080/page2"
  },
  {
    "name": "minScore",
    "expected": 0.5,
    "actual": 0.64,
    "values": [
      0.64,
      0.64,
      0.64
    ],
    "operator": ">=",
    "passed": true,
    "auditProperty": "seo",
    "auditId": "categories",
    "level": "error",
    "url": "http://localhost:8080/page2"
  }
]
Asked By: Seer

||

Yes, that’s what the [] iterator does, you get one JSON file for each. To get an array, you can enclose the whole expression in [...] to make an array out of it or use map() to modify the input array in place instead of [] to iterate over its elements.

$ echo '[1,2,3,4]' | jq '.[]|select(.<3)'
1
2
$ echo '[1,2,3,4]' | jq '[.[]|select(.<3)]'
[
  1,
  2
]
$ echo '[1,2,3,4]' | jq 'map(select(.<3))'
[
  1,
  2
]

Having comma-separated elements without the enclosing [, ] to make it an array would make it invalid json, but if that’s what you want, you can pipe it to sed '1d;$d' to delete the first and last lines.

Answered By: Stéphane Chazelas

You seem to want to modify the entries that have auditId set to the string categories. What you are doing is to extract those elements from the top-level array and modify them. This gives you a set (not an array) of objects.

Instead of using | to extract elements from the array, use |= to modify its elements. See the end of this answer for how to properly insert the resulting array into a JSON structure in another file.

So, use

.[] |= (
    select(.auditId == "categories") |
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": "*(.auditProperty):* (.actual) (expected (.expected))"
        }
    }
)

Alternatively, don’t use .[] at all and instead use map() with the expression on each of the elements in the top-level array:

map(
    select(.auditId == "categories") |
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": "*(.auditProperty):* (.actual) (expected (.expected))"
        }
    }
)

These expressions will give you an array of elements like this:

[
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "*performance:* 1 (expected 0.8)"
    }
  },
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "*accessibility:* 1 (expected 0.9)"
    }
  },
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "*best-practices:* 0.96 (expected 0.9)"
    }
  },
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "*seo:* 0.64 (expected 0.5)"
    }
  }
]

To add this array to an already existing array in another file, you can do this (assuming there is an array key at the top-level of otherfile and that your input is in inputfile; the key will be created if it does not already exist):

jq '.array += ( input |
    map(
        select(.auditId == "categories") |
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "*(.auditProperty):* (.actual) (expected (.expected))"
            }
        }
    )
)' otherfile inputfile

When input is executed, the array from the second file on the command line is read.

Change += to just = if you want to replace the existing array key’s value instead of adding to it.

Answered By: Kusalananda
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.