JSONata query to flatten array of arrays - jsonata

The JSONata doc "top-level-arrays-nested-arrays-and-array-flattening" covers the "flatten" case of an array of objects, each of which contains a property that contains an array value.
However, I have not been able to figure out how to flatten an array of arrays.
Q: What is the JSONata query to flatten an array of arrays?
input
[ [1,2], [], [3] ]
desired
[ 1, 2, 3 ]

I have figured out that flattening an array of arrays can be accomplished by using the $reduce function to iteratively apply the $append function.
$reduce($, $append)
for this simple test case:
$reduce( [ [1,2], [], [3] ], $append)
Q: Are there other ways to flatten an array of arrays in JSONata?

In JSONata, iterating over all the elements of an array returns a flattened array of the elements appended together... So it's really as simple as:
$.*
Almost looks like an emoji! ;*)
Technically, you don't even need the $. prefix -- but just using the expression * doesn't look right to me...

Related

JSONata prevent array flattening

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.
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).
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?
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.

Reduce nested array to string?

I have the following array:
arr = [["Example"]]
I need to reduce it to just "Example" (basically, just remove the array).
I know I could do arr[0][0], but am curious if there's a simple method to just remove the string from the array without using indexes.
For clarification...there will only ever be a single item in the array.
For a single item, you can use:
[['array']].join
=> 'array'
Updated with more examples
If you have multiple items, the strings will be combined:
[['array'], ['array']].join
=> 'arrayarray'
And if you pass a parameter to the join method:
[['array'], ['array']].join('&')
=> 'array&array'
While this is not as efficient as [0][0], it will still work:
arr.flatten.first

How to count the elements of an array inside an array?

I have a method which returns the number of hotels from a webpage:
hotel_count = self.getHotelsList.values
The output of this method is:
[["hotel_0", "hotel_1", "hotel_2", "hotel_3", "hotel_4", "hotel_5", "hotel_6", "hotel_7", "hotel_8", "hotel_9", "hotel_10", "hotel_11", "hotel_12", "hotel_13", "hotel_14", "hotel_15", "hotel_16", "hotel_17", "hotel_18", "hotel_19", "hotel_20", "hotel_21", "hotel_22", "hotel_23", "hotel_24", "hotel_25", "hotel_26", "hotel_27", "hotel_28", "hotel_29", "hotel_30", "hotel_31", "hotel_32", "hotel_33", "hotel_34", "hotel_35", "hotel_36", "hotel_37", "hotel_38", "hotel_39", "hotel_40"]]
I want to know the length of this array, but if I write
hotel_count = self.getHotelsList.values.length
The length is 1. How can I get a length of 41, which is the one I'm expecting?
Thanks
The array you are showing is nested inside another array. So the outer array is of length 1, the inner array is what you want.
To get it you have to first get the first element of the outer array using [0] or first
testList[0].length
testList.first.length
I am not sure why your getHotelsList method returns a nested array, it doesn't appear to need it.
hotel_count = getHotelsList.values.first.length
You can also do it with [0], but first is faster.
Two notes:
You don't need self at the beginning.
It is a bad habit to use camel case for method names in Ruby. it should better be get_hotels_list.
You could convert that into a single array with flatten:
hotel_count = self.getHotelsList.values.flatten.size

Sort an array by value

I have an array like this:
array = ["git-hw-abcd", "svn-hw", "svn-hw-design","git-hw"]
If I do array.sort I would basically get an ascending sort like this
["git-hw", "git-hw-abcd", "svn-hw", "svn-hw-design"]
Would like to sort the array by the values "svn-hw" and "git-hw" to appear as the first and second elements of the array so that I would get:
[ "svn-hw","git-hw",... then the rest of the values]
Any help would be appreciated.
Try this
* sorting on length(as sort criteria is not specified)
["git-hw-abcd", "svn-hw", "svn-hw-design","git-hw"].sort{|a,b| a.length <=> b.length}
It returns
["git-hw", "svn-hw", "git-hw-abcd", "svn-hw-design"]
array.sort{|a,b| a.split("-h")[1]<=>b.split("-h")[1]}
this is what you want
#Hivltg
try the length order using this :array= [ "svn-hw", "git-hw-abcd","git-hw", "svn-hw-design", "git-hw-bassics"]
you will find out the problem.
Not exactly sure how you're ordering, but here's an example of using a block to sort
array.sort{|a,b| a[4..-1] <=> b[4..-1]}

Add all Arrays with an Main Array without duplicates

I need to add an array of array in to a single array without duplicates
array = [[1,2,3],[2,3,4],[7,8,9]]
to
new_array [1,2,3,4,7,8,9]
What is the best possible way to do IT in Ruby.
Try this:
array.flatten!.uniq!
flatten! takes any sub-arrays and adds their elements to the enclosing array (recursively), so it "flattens out" arrays of arrays.
uniq! removes duplicate elements from the array.
Note that ! methods modify the original array. Use the non-! methods (flatten and uniq) if you wish to return a new array instead.

Resources