3

i have the following json

{
    "name" : "qwerty",
    "values" :[
    {
        "field1" : [
            "val1"
            ],
        "field2" : [
            "val2"
            ],
        "name1" : [["a", "b"], ["c", "d"]]
    },
    {
        "field1" : [
            "val3"
            ],
        "field2" : [
            "val4"
            ],
        "name1" : [["a", "b"], ["c", "d"]]
    },
    {
        "field1" : [
            "val5"
            ],
        "field2" : [
            "val6"
            ],
         "name1" : [["a", "b"], ["c", "d"]]
    }
    ]
}

I need to change the above json to the following using jq in bash

{
    "name" : "qwerty",
    "values" :[
    {
        "field1" :  "val1",
        "field2" :  "val2",
        "new_name" : [["a", "b"], ["c", "d"]]
    },
    {
        "field1" : "val3",
        "field2" : "val4",
        "new_name" : [["a", "b"], ["c", "d"]]
    },
    {
        "field1" : "val5",
        "field2" : "val6",
        "new_name" : [["a", "b"], ["c", "d"]]
    }
    ]

}

Here i am facing the following issues :

I tried parsing the inner json with tag values and replace the '[' ']' with spaces, however, when i try to put the "values" in a variable in the form of list, jq is prettifying and then showing each new line as a element of an array.

The number of inner jsons in the values array is not fixed.

Can some one please help me with framing the jq statement to be run in bash to make the required changes.

vamsi
  • 325
  • 5
  • 15
  • 1
    @fedorqui thnx for pointing that out, corrected the question – vamsi Aug 18 '16 at 10:12
  • Do you know that it is specifically `.values[].field1` and `.values[].field2` you want to change, or is it any singleton list anywhere that should be collapsed to a single value? – chepner Aug 18 '16 at 11:50
  • Yes "field1, field2, name" are fixed in each json. But the number of jsons is not fixed. – vamsi Aug 18 '16 at 12:02
  • @vamsi I have edited my answer, it should be fine now – Tiago Lopo Aug 18 '16 at 12:10
  • sorry for reopening the question. There is a alight change in my requirement, i would like to know if this can be done if we dont have the number of fields also fixed. That is "field1, field2, name" are also not fixed in each json. – vamsi Aug 24 '16 at 06:25
  • .values[] |= (.[] |= .[0]) solved the issue :) – vamsi Aug 24 '16 at 10:37

3 Answers3

2

The snippet below should do what you want:

jq '{
    "name": .name, 
    "values": [ 
        {
            "field1" : .values[0].field1[0], 
            "field2" : .values[0].field2[0],
            "New_name": .values[0].name1  
        },
        {
            "field1" : .values[1].field1[0],
            "field2" : .values[1].field2[0],
            "new_name" : .values[1].name1
        },
        {
            "field1" : .values[2].field1[0],
            "field2" : .values[2].field2[0],
            "new_name" : .values[2].name1
        }
    ]  
}' < /tmp/input.json

EDIT

Since the number of objects are not fixed the snippet below will do:

jq '{ 
        "name" : .name,
        "values" : [
                .values[] as $in | 
                { 
                        "field1" : $in.field1[0],
                        "field2" : $in.field2[0],
                        "new_name" : $in.name1 
                }
        ]

}' < /tmp/input.json
Tiago Lopo
  • 7,619
  • 1
  • 30
  • 51
  • thnx for the quick response. But, the number of inner jsons in the values array is not fixed. – vamsi Aug 18 '16 at 10:54
2

This should work; I'm not sure if there is a way to refactor the assignments to field1 and field2:

jq '.values[] |= (.field1=.field1[0] | .field2=.field2[0])' tmp1.json
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Another option is to create an object with the new values and merge it. But I think that's uglier and potentially more destructive than the assignments. – Jeff Mercado Aug 18 '16 at 16:01
  • Based on the name alone, I thought `foreach` might be relevant, but I can't seem to wrap my mind around how it works. It was all I could do to come up with something like `jq '.values[] |= (foreach ["field1", "field2"] as $f (empty;.[$f]=.[$f][0];empty))' tmp1.json` that at least complies; it produces no output. – chepner Aug 18 '16 at 16:05
  • In my head, the way I see it is `foreach` is like `reduce` but with an additional extraction part. So in your example in your comment, you initialized with an empty value, and try to assign values to it. But empty is empty so it yields no results for every iteration. Then for the extraction part, you extracted empty so nothing comes from it. With that approach, you could just stick with `reduce` (with a slight adjustment): `.values[] |= reduce ("field1", "field2") as $f (.;.[$f]=.[$f][0])` – Jeff Mercado Aug 18 '16 at 16:16
  • OK, nice. I think I understand `reduce` a little better now. – chepner Aug 18 '16 at 16:43
  • @JeffMercado But the output json being created is not the one i want, though it has the changes required. Being new to jq, I tried playing around with reduce (jq '. | reduce("field1","field2") as $f (.values[];.[$f]=.[$f][0])' tmp1.json) , but it didnt work. – vamsi Aug 18 '16 at 17:47
  • @vamsi Sorry, I was eyeballing the output and though I saw that it was the same. – chepner Aug 18 '16 at 17:55
  • 1
    Did some thinking and here's another way you could modify the multiple values: `.values[] |= (.["field1", "field2"] |= .[0])`. – Jeff Mercado Aug 19 '16 at 17:39
  • @JeffMercado sorry for reopening the question, but i would like to know if this can be done if we dont have the number of fields also fixed. That is "field1, field2, name" are also not fixed in each json. – vamsi Aug 24 '16 at 06:24
0

Here is another solution.

  .values |= map({
     field1:   .field1[0],
     field2:   .field2[0],
     new_name: .name1
  })
jq170727
  • 13,159
  • 3
  • 46
  • 56