6

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"
  }
]

2 Answers 2

9

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.

4
  • Thanks for the super thorough answers! Both examples return an actual array which is not what I want though. Its Slack's horrible blockkit structure I am trying to map into and the result of my expression will be inserted into an already created blocks array
    – Seer
    Apr 23 at 11:51
  • @Seer I think you omitted reading the end of my answer.
    – Kusalananda
    Apr 23 at 11:55
  • 1
    you are correct, I did miss that!! Yes nice!!! A much nicer way of approaching than my horrible EOF stuff previously too. Much appreciated!!
    – Seer
    Apr 23 at 12:33
  • I regret that I have only one upvote to give this answer. Apr 23 at 15:58
5

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.

1
  • yes, bizzarely it is invalid json I want :) Its just a list of block entities to add inside an already created blocks array which already has some static items.
    – Seer
    Apr 23 at 11:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .