I am trying to create an array of objects in bash given an array in bash using jq.
Here is where I am stuck:
IDS=("baf3eca8-c4bd-4590-bf1f-9b1515d521ba" "ef2fa922-2038-445c-9d32-8c1f23511fe4")
echo "${IDS[#]}" | jq -R '[{id: ., names: ["bob", "sally"]}]'
Results in:
[
{
"id": "baf3eca8-c4bd-4590-bf1f-9b1515d521ba ef2fa922-2038-445c-9d32-8c1f23511fe4",
"names": [
"bob",
"sally"
]
}
]
My desired result:
[
{
"id": "baf3eca8-c4bd-4590-bf1f-9b1515d521ba",
"names": [
"bob",
"sally"
]
},
{
"id": "ef2fa922-2038-445c-9d32-8c1f23511fe4",
"names": [
"bob",
"sally"
]
}
]
Any help would be much appreciated.
Split your bash array into NUL-delimited items using printf '%s\0', then read the raw stream using -R or --raw-input and within your jq filter split them into an array using split and the delimiter "\u0000":
printf '%s\0' "${IDS[#]}" | jq -Rs '
split("\u0000") | map({id:., names: ["bob", "sally"]})
'
for id in "${IDS[#]}" ; do
echo "$id"
done | jq -nR '[ {id: inputs, names: ["bob", "sally"]} ]'
or as a one-liner:
printf "%s\n" "${IDS[#]}" | jq -nR '[{id: inputs, names: ["bob", "sally"]}]'
Related
I have an input row like this: 1374240, 1374241. I need to make json file:
{
"version": "1.0",
"tests": [
{
"id": 1374240,
"selector": ""
},
{
"id": 1374241,
"selector": ""
}
]
}
I maked associated array:
idRow='1374240, 1374241'
IFS=',' read -r -a array <<<"$idRow"
trimmedArray=()
for id in "${array[#]}"; do
trimmedId="$(echo -e "${id}" | xargs)"
testRow="{\"id\":${trimmedId},\"selector\":\"\"}"
trimmedArray+=("$testRow")
done
echo "${trimmedArray[*]}"
Output:
{"id":1374240,"selector":""} {"id":1374241,"selector":""}
How i can insert it in final json structure and write a file?
I am tried a different variants with jq, but I can`t get finally structure. Please, help.
Read in the numbers as raw text using -R, split at the ,, use tonumbers to convert them to numbers, and create the structure on the fly:
echo "1374240, 1374241" | jq -R '
{version:"1.0",tests:(
split(",") | map(
{id: tonumber, selector: ""}
)
)}
'
Demo
If you can omit the comma in the first place, it's even easier to read in numbers as they itself are JSON:
echo "1374240 1374241" | jq -s '
{version:"1.0",tests: map(
{id: tonumber, selector: ""}
)}
'
Demo
Output:
{
"version": "1.0",
"tests": [
{
"id": 1374240,
"selector": ""
},
{
"id": 1374241,
"selector": ""
}
]
}
trying to access a field in the list array via jq. The fields doesnt have a name for me to gain access to and extract. Please assist?
Trying to extract John and Smith.
$ cat test.txt
{
"content": {
"list": [
[
[
"name",
"John",
123
],
[
"surname",
"Smith",
345
],
1
]
]
}
}
$ jq -r '.content | {name: ."list"}' test.txt
{
"name": [
[
[
"name",
"John",
123
],
[
"surname",
"Smith",
345
],
1
]
]
}
You could do something as naive as:
$ jq -r '.content.list[][][1]?' test.json
John
Smith
Which will extract the second field from the array third nested arrays, and ignore the numeric literal.
Alternative you could manipulate the data before-hand to make it easier to manipulate afterwards:
$ jq '.content.list | map(map({ (.[0]): .[1] }?) | add)'
[
{
"name": "John",
"surname": "Smith"
}
]
Extracting the name(s) would be as simple as just using | [].name:
$ jq '.content.list | map(map({ (.[0]): .[1] }?) | add) | .[].name'
"John"
I currently have the following JSON output from echo $items | jq:
{
"Family_Name": "Type 1",
"Quantity_On_Hand": "335"
}
{
"Family_Name": "Type 2",
"Quantity_On_Hand": "215"
}
{
"Family_Name": "Type 9",
"Quantity_On_Hand": "159"
}
{
"Family_Name": "Type 4",
"Quantity_On_Hand": "500"
}
I also have a bash array colors of the same size looking like
"Blue" "Red" "Green" "Blue"
How can I use jq so that I get something like
{
"Family_Name": "Type 1",
"Quantity_On_Hand": "335",
"Colors": "Blue"
}
{
"Family_Name": "Type 2",
"Quantity_On_Hand": "215",
"Colors": "Red"
}
{
"Family_Name": "Type 9",
"Quantity_On_Hand": "159",
"Colors": "Green"
}
{
"Family_Name": "Type 4",
"Quantity_On_Hand": "500",
"Colors": "Blue"
}
I tried using something like jq --arg or jq -n '.{} |= [$colors]' but cannot get it correct.
Using jq 1.5 or later, with your input (i.e., a stream of JSON objects) and
colors=("Blue" "Red" "Green" "Blue")
the following produces an array of the desired JSON objects:
jq -s '
($ARGS.positional | map({Colors: .})) as $colors
| [., $colors] | transpose | map(add)
' --args "${colors[#]}"
If you want the result to be a stream of the JSON objects, you could tack on [] or
change transpose | map(add) to transpose[] | add
Caveats
(1) The above solution will work even if some colors have spaces in their names, but in general it may be necessary to pass in the contents of the array using some other mechanism.
(2) If your jq does not support positional parameters (--args), now may be a good time to upgrade; if that is not an option, you could devise a workaround, e.g. using the --argfile option if your jq supports that.
Here's a solution that has been tested with versions of jq from 1.4 onwards:
jq -R -s --argfile json <(echo "$items") '
(split("\n") | map(select(length>0))) as $colors
| [ range(0; $colors|length) | $json[.] + {Colors: $colors[.]} ]
' <( printf "%s\n" "${colors[#]}" )
The subtlety here is that -argfile will combine the stream of JSON objects into a single array.
So my objective is to merge json files obtain this format:
{
"title": "NamesBook",
"list": [
{
"name": "Ajay"
},
{
"name": "Al"
}
]
}
And I have files that look like this format:
blahblah.json
{
"title": "NamesBook",
"list": [
{
"name": "Ajay"
}
]
}
blueblue.json
{
"title": "NamesBook",
"list": [
{
"name": "Al"
}
]
}
I can store the list array of all my names in a variable with the following:
x = jq -s '.[].list' *.json
And then I was planning on appending the variable to an empty array in a file I created, out.json, which looks like this:
{
"type": "NamesBook",
"list": []
}
However, when my script runs over the line
jq '.list[] += "$x"' out.json'
It brings up a jq error:
Cannot iterate over null.
Even when I add a random element, the same error shows up. Tips on how I should proceed? Are there other tools in jq to help achieve merging arrays?
Let me also provide just what the title asks for, because I'm sure a lot of people that stepped on this question look for something simpler.
Any of the following (added math2001 and pmf answers):
echo -e '["a","b"]\n["c","d"]' | jq -s 'add'
echo -e '["a","b"]\n["c","d"]' | jq -s 'flatten(1)'
echo -e '["a","b"]\n["c","d"]' | jq -s 'map(.[])'
echo -e '["a","b"]\n["c","d"]' | jq -s '[.[][]]'
echo -e '["a","b"]\n["c","d"]' | jq '.[]' | jq -s
results in:
[
"a",
"b",
"c",
"d"
]
Note: Also any of the above can apply to arrays of objects.
You can merge your files with add (jq 1.3+):
jq -s '.[0].list=[.[].list|add]|.[0]' *.json
or flatten (jq 1.5+):
jq -s '.[0].list=([.[].list]|flatten)|.[0]' *.json
[.[].list] - creates an array of all "list" arrays
[
[
{
"name": "Ajay"
}
],
[
{
"name": "Al"
}
]
]
[.[].list]|flatten - flatten it (or .[].list|add - add all the arrays together)
[
{
"name": "Ajay"
},
{
"name": "Al"
}
]
.[0].list=([.[].list]|flatten)|.[0] - replace the first "list" with the merged one, output it.
{
"title": "NamesBook",
"list": [
{
"name": "Ajay"
},
{
"name": "Al"
}
]
}
Assuming every file will have the same title and you're simply combining the list contents, you could do this:
$ jq 'reduce inputs as $i (.; .list += $i.list)' blahblah.json blueblue.json
This just takes the first item and adds to its list, the list of all the other inputs.
The OP did not specify what should happen if there are objects for which .title is not "NamesBook". If the intent is to select objects with .title equal to "NamesBook", one could write:
map(select(.title == "NamesBook"))
| {title: .[0].title, list: map( .list ) | add}
This assumes that jq is invoked with the -s option.
Incidentally, add is the way to go here: simple and fast.
This sounds like a basic question but I have been searching for a better solution for some time already...
I have the following files:
file0.json:
{
"d": {
"aaData": [
{"a":1},
{"a":2},
{"a":3},
{"a":4}
]
}
}
and
file1.json:
[
{"b":1},
{"b":2},
{"b":7}
]
and I want a subtraction between then with the expected result:
{
"key": 3
}
{
"key": 4
}
I thought of the following command would do the job:
bash$ jq -s '[.d.aaData[].a] - [.[].b] | { key: .}' file0.json file1.json
but it gives me the following error:
Cannot index array with string "b"
What makes sense, since [.[1].b] would't create an array, but something like [1][2][7]
the only way I managed to get the expected result was coding in my bash script the dirty solution:
bash$ a=$(jq '[.d.aaData[].a] | {key1: .}' file0.json)
bash$ b=$(jq '[.[].b] | {key2: . }' file1.json)
bash$ c=$(echo -n $a,$b)
bash$ echo $c
{ "key1": [ 1, 2, 3, 4 ] },{ "key2": [ 1, 2, 7 ] }
bash$ d=${c//"},{"/","}
bash$ echo $d
{ "key1": [ 1, 2, 3, 4 ] , "key2": [ 1, 2, 7 ] }
bash$ echo -n $d | jq '.key1 - .key2 | { key: .[] }'
{
"key": 3
}
{
"key": 4
}
I'm quite sure there is a better way to do it with jq.... just can't find how...
Select only the values in a that are different from all values in b.
jq -n --argfile a file0.json --argfile b file1.json '
$a.d.aaData[].a as $a
| select([$b[].b]
| all(. != $a))
| {key: $a}'
Here is a solution which uses the jq - array difference operator:
[ $file0.d.aaData[] | {key:.a} ]
- [ $file1[] | {key:.b} ]
| .[]
Assuming filter.json contains this filter then
jq -M -n --argfile file0 file0.json --argfile file1 file1.json -f filter.json
produces
{
"key": 3
}
{
"key": 4
}