bosskay972
bosskay972

Reputation: 993

Is it possible to make nested query in jq with JSON object?

Here's my JSON:

{  
   "A":[  
      {  
         "AT":"text"
      },
      {  
         "AT":"text2"
      }
   ],
   "B":[  
      {  
         "name":"text",
         "power":10
      },
      {  
         "name":"text1",
         "power":20
      },
      {  
         "name":"text2",
         "power":40
      }
   ]
}

I want to have all B element with the same name as A element. So in this case the result would be ['text',10,'text2',40]. I try this query [.B[] | [.name , .power][]] | map(in([.A[].AT]))but it doesn't work it's throw me an error:

jq: error (at :23): Cannot index string with string "A" exit status 5

I don't know how to fix it but I think it's about the parent node, it doesn't recognize the nested query in the in function. So is it possible to make nested query in this language and how can handle a problem with nested query for other case like other function or other situation as pipe ?

PS: I work in JAVASCRIPT (node.js)

New problem, I have this query: ( .Move | map({(.name): {accuracy : .url.accuracy, damage_class: .url.damage_class.name, power : .url.power, priority: .url.priority, target: .url.target.name, type: .url.type.name, null}}) | add ) as $b | .results | select(.name=="bulba") | map(.url.moves[].move.name | [., $b[.][]]) | add

and it's work perfectly without select(.name=="bulba")

jq: error (at :158): Cannot index array with string "name"

Should I show the JSON with this ?(because is a little bigger than the previous one) But it's start with this:

    {  
   "results":[  
      {  
         "name":"bulba",
         "url":{  
            "moves":[  
               {  
                  "move":{  
                     "name":"flamiche"
                  }
               }
            ]
         }
      },
      {  
         "name":"sala",
         "url":{  
            "moves":[  
               {  
                  "move":{  
                     "name":"DAMSSSS"
                  }
               }
            ]
         }
      }
   ],
   "Move":[  
      {  
         "name":"flamiche",
         "url":{  
            "accuracy":50,
            "damage_class":{  
               "name":"physical"
            },
            "power":50,
            "priority":0,
            "target":{  
               "name":"foe"
            },
            "type":{  
               "name":"fire"
            }
         }
      },
      {  
         "name":"DAMSSSS",
         "url":{  
            "accuracy":90,
            "damage_class":{  
               "name":"status"
            },
            "power":null,
            "priority":2,
            "target":{  
               "name":"self"
            },
            "type":{  
               "name":"grass"
            }
         }
      }
   ]
}

Upvotes: 3

Views: 2498

Answers (2)

oguz ismail
oguz ismail

Reputation: 50750

You're piping [.B[] | [.name , .power][]]'s product --which is to be an array of strings-- to map(in([.A[].AT])), i.e input is changed and .A is lost, and you're trying to get .A[].AT from those strings. This is not the only issue but the reason why you get that error. You better do something like this:

( .B | map({(.name): .power}) | add ) as $b
| .A | map(.AT | [., $b[.]]) | add

If your input is too big using reduce you can get better performance:

( reduce .B[] as $b ({}; . + ($b | {(.name): .power})) ) as $b
| reduce .A[].AT as $a ([]; . + [$a, $b[$a]])

As for your second question, by passing results, an array of objects, to select(.name=="bulba"), as the error message states you're trying to index an array with a string. To filter objects under results move select into map like:

map(select(.name == "bulba") | .url.moves[].move.name | [., $b[.][]])

Upvotes: 2

Eddie
Eddie

Reputation: 26844

If you want to get all B objects that are on A, you can use Set and concat to create an object with all the A values. Use filter to filter the object B

var obj = {"A":[{"AT":"text"},{"AT":"text2"}],"B":[{"name":"text","power":10},{"name":"text1","power":20},{"name":"text2","power":40}]}

var allAs = new Set([].concat(...obj.A.map(o => Object.values(o))));
var result = obj.B.filter(o => allAs.has(o.name));

console.log(result);

You can also use flatMap (available on Node 11.0.0) as:

var obj = {"A":[{"AT":"text"},{"AT":"text2"}],"B":[{"name":"text","power":10},{"name":"text1","power":20},{"name":"text2","power":40}]}

var allAs = new Set(obj.A.flatMap(o => Object.values(o)));
var result = obj.B.filter(o => allAs.has(o.name));

console.log(result);

Upvotes: 0

Related Questions