0

Q: How do I prevent JSONata from "auto-flattening" arrays in an array constructor?

Given JSON data:

{
 "w" : true,
 "x":["a", "b"],
 "y":[1, 2, 3],
 "z": 9
}

the JSONata query seems to select 4 values:

[$.w, $.x, $.y, $.z]

The nested arrays at $.x and $.y are getting flattened/inlined into my outer wrapper, resulting in more than 4 values:

[ true, "a", "b", 1, 2, 3, 9 ]

The results I would like to achieve are

[ true, ["a", "b"], [1, 2, 3], 9 ]

I can achieve this by using

[$.w, [$.x], [$.y], $.z]

But this requires me to know a priori that $.x and $.y are arrays.

I would like to select 4 values and have the resulting array contain exactly 4 values, independent of the types of values that are selected.

There are clearly some things about the interactions between JSONata sequences and arrays that I can't get my head around.

asked Apr 4, 2018 at 12:09
1
  • In my struggle to understand the behavior of array construction in JSONata I found https://github.com/jsonata-js/jsonata/issues/93 to provide some useful context. In the spirit of providing constructive feedback, I want to let you know that I am having a great deal of difficulty understanding the model of array vs. XPath/XQuery-like-sequence behavior. Commented Apr 4, 2018 at 13:42

3 Answers 3

1

In common with XPath/XQuery sequences, it will flatten the results of a path expression into the output array. It is possible to avoid this in your example by using the $each higher-order function to iterate over an object's key/value pairs. The following expression will get what you want without any flattening of results:

$each(,ドル function($v) {
 $v
})

This just returns the value for each property in the object.

UPDATE: Extending this answer for your updated question: I think this is related to a previous github question on how to combine several independent queries into the same question. This uses an object to hold all the queries in a similar manner to the one you arrived at. Perhaps a slightly clearer expression would be this:

{
 "1": t,
 "2": u.i,
 "3": u.j,
 "4": u.k,
 "5": u.l,
 "6": v
} ~> $each(λ($v){$v})

The λ is just a shorthand for function, if you can find it on your keyboard (F12 in the JSONata Exerciser).

answered Apr 4, 2018 at 16:12
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your prompt response. OK ... my simple example did not adequately demonstrate the problem. Your explanation shows me how to use $each if I want to iterate all of the values in the object. I actually don't want to select all items, but want to select n specific sub-trees and collect them into an array of length n. I will post a better example.
0

I am struggling to rephrase my question in such as way as to describe the difficulties I am having with JSONata's sequence-like treatment of arrays.

I need to run several queries to extract several values from the same JSON tree. I would like to construct one JSONata query expression which extracts n data items (or runs n subqueries) and returns exactly n values in an ordered array.

This example seems to query request 6 values, but because of array flattening the result array does not have 6 values.

This example explicitly wraps each query in an array constructor so that the result has 6 values. However, the values which are not arrays are wrapped in an extraneous & undesirable array. In addition one cannot determine what the original type was ...

This example shows the result that I am trying to accomplish ... I asked for 6 things and I got 6 values back. However, I must know the datatypes of the values I am fetching and explicitly wrap the arrays in an array constructor to work-around the sequence flattening.

This example shows what I want. I queried 6 things and got back 6 answers without knowing the datatypes. But I have to introduce an object as a temporary container in order to work around the array flattening behavior.

I have not found any predicates that allow me to test the type of a value in a query ... which might have let me use the ?: operator to dynamically decide whether or not to wrap arrays in an array constructor. e.g. $isArray($.foo) ? [$.foo] : $.foo

Q: Is there an easier way for me to (effectively) submit 6 "path" queries and get back 6 values in an ordered array without knowing the data types of the values I am querying?

answered Apr 5, 2018 at 3:36

Comments

0

Building on the example from Acoleman, here is a way to pass in n "query" strings (that represent paths):

(['t', 'u.i', 'u.j', 'u.k', 'u.l', 'v'] {
 $: $eval('$$.' & $)
}).$each(function($o) {$o})

and get back an array ofn results with their original data format:

[
 12345,
 [
 "i",
 "ii",
 "iii"
 ],
 [],
 "K",
 {
 "L": "LL"
 },
 null
]

It seems that using $each is the only way to avoid any flattening...

Granted, probably not the most efficient of expressions, since each has to be evaluated from a path string starting at the root of the data structure -- but there ya go.

answered Aug 27, 2021 at 16:04

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.