Skip to content

Commit ba922fd

Browse files
authored
Merge branch 'schibsted:master' into feature/upgrade-dependencies
2 parents b21f406 + 40392a2 commit ba922fd

File tree

5 files changed

+187
-15
lines changed

5 files changed

+187
-15
lines changed

core/src/main/java/com/schibsted/spt/data/jslt/impl/NodeUtils.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,6 @@ private static JsonNode parseNumber(String number) {
159159
int intStart = pos;
160160

161161
int endInteger = scanDigits(number, pos);
162-
if (endInteger == pos)
163-
return null;
164162
if (endInteger == number.length()) {
165163
if (number.length() < 10)
166164
return new IntNode(Integer.parseInt(number));
@@ -172,7 +170,12 @@ else if (number.length() < 19)
172170

173171
// since there's stuff after the initial integer it must be either
174172
// the decimal part or the exponent
175-
long intPart = Long.parseLong(number.substring(intStart, endInteger));
173+
long intPart;
174+
if (endInteger == pos)
175+
intPart = 0; // this means there was no zero before the period
176+
else
177+
intPart = Long.parseLong(number.substring(intStart, endInteger));
178+
176179
pos = endInteger;
177180
double value = intPart * sign;
178181

core/src/test/resources/function-error-tests.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,6 @@
6262
"query": "number(\"123.\")",
6363
"input": "{}"
6464
},
65-
{
66-
"error": ".345",
67-
"query": "number(\".345\")",
68-
"input": "{}"
69-
},
7065
{
7166
"error": "1.1e",
7267
"query": "number(\"1.1e\")",

core/src/test/resources/function-tests.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,16 @@
762762
"input" : "{}",
763763
"output" : "-10.0"
764764
},
765+
{
766+
"query" : "number(\".1E2\")",
767+
"input" : "{}",
768+
"output" : "10.0"
769+
},
770+
{
771+
"query" : "number(\"-.1E2\")",
772+
"input" : "{}",
773+
"output" : "-10.0"
774+
},
765775
{
766776
"query" : "number(\"0.1e-2\")",
767777
"input" : "{}",
@@ -837,6 +847,16 @@
837847
"input" : "{}",
838848
"output" : "-0.513"
839849
},
850+
{
851+
"query" : "number(\"-.513\")",
852+
"input" : "{}",
853+
"output" : "-0.513"
854+
},
855+
{
856+
"query" : "number(\".513\")",
857+
"input" : "{}",
858+
"output" : "0.513"
859+
},
840860
{
841861
"input" : "{}",
842862
"output" : "2",

docs/sorting.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
2+
# Sorting
3+
4+
The need for being able to sort JSON output comes up repeatedly, and
5+
is a natural need. The question is how to work it into the language.
6+
7+
## What can be sorted?
8+
9+
Only arrays can be sorted.
10+
11+
## Syntax
12+
13+
Two obvious approaches present themselves immediately: a macro or an
14+
`order by` operator.
15+
16+
### Custom syntax
17+
18+
One could imagine adding an `order by` to list comprehensions:
19+
20+
```[for (...) ... if (...) order by ... asc|desc]```
21+
22+
The same for object comprehensions doesn't make sense.
23+
24+
It would also be possible to have `order by` as simply an operator:
25+
26+
```.foo.bar order by .key```
27+
28+
Formally: `<expr> order by <expr>`, with priority to be established.
29+
30+
### Macro
31+
32+
A macro `sort` would work:
33+
34+
```sort(<array to sort>, <expression to produce sort key>?)```
35+
36+
Thus:
37+
38+
```
39+
sort([5, 1, 3]) => [1, 3, 5]
40+
sort([5, 1, 3], -1 * .) => [5, 3, 1]
41+
```
42+
43+
One could also imagine a third parameter to choose between ascending
44+
and descending sort. However, a `reverse()` function might be useful
45+
for other purposes, too, so perhaps it would be better to add that,
46+
too.
47+
48+
## Value ordering
49+
50+
Numbers are easy: ordered according to natural numeric order.
51+
52+
Strings are also easy: ordered according to Unicode code point. We
53+
make no attempt to support custom collations based on language or
54+
other preferences. Anyone wanting that will need to define functions
55+
to generate sort keys.
56+
57+
Booleans: `false` sorts before `true`.
58+
59+
Arrays: sorted according the nth element, beginning with the first. If
60+
the nth element of one array is smaller, then so is the array. If one
61+
array has no element in the nth position, that array is smaller. If
62+
the elements are equal then comparison moves to `n+1`.
63+
64+
Objects? Sorting them is not really very nice. Python 3 does not allow
65+
sorting dicts at all, but Python 2 did. However, refusing to sort
66+
arrays with mixed types can easily lead to errors if the input is not
67+
shaped as expected, and we generally try to avoid that. So objects
68+
should be sortable.
69+
70+
We don't really care about the order of objects, so for the sake of
71+
speed objects compare by size first. Then by the smallest key. Then by
72+
the value of the smallest key. If the smallest keys are the same
73+
comparison moves to the next key.
74+
75+
The order of the types is `null`, booleans, numbers, strings, arrays,
76+
objects.
77+
78+
# Use case solutions
79+
80+
## Original motivation
81+
82+
From [the original issue](https://github.com/schibsted/jslt/issues/172#issue-781566400) we have this example: "e.g. you can sort by
83+
the score field in the following example:"
84+
85+
```[ { "id": "a", "score": 4}, { "id": "b", "score": 3}]```
86+
87+
There is some missing context here, but with an operator this would be:
88+
89+
```. order by .score```
90+
91+
With a macro it would be:
92+
93+
```sort(., .score)```
94+
95+
Github user hemakumarg89 also wanted to sort "json objects in an
96+
array(based on a field value)". That's basically the same need.
97+
98+
CONSIDER THE SAME THING IN A MORE EMBEDDED CONTEXT
99+
100+
## StackOverflow example
101+
102+
From StackOverflow comes [a slightly more advanced example](https://stackoverflow.com/questions/76236450/jslt-are-there-global-variables).
103+
104+
There is a defined ranking of keys:
105+
106+
```let ranking = {
107+
"DEC": 0,
108+
"SBS": 1,
109+
"CON": 2,
110+
"GCS": 3,
111+
"GMS": 4,
112+
"FXP": 5,
113+
"QAN": 6,
114+
"REF": 7,
115+
"PRO": 8
116+
}
117+
```
118+
119+
The input (presumably) looks something like:
120+
121+
```
122+
[
123+
{..., "tag" : "CON"},
124+
{..., "tag" : "REF"},
125+
{..., "tag" : "DEC"},
126+
...
127+
]
128+
```
129+
130+
and we want the value with the lowest ranking.
131+
132+
With an operator this would be:
133+
134+
```
135+
{
136+
"data": {
137+
"segment": (.payload order by get-key($ranking, .tag)) [0]
138+
}
139+
}
140+
```
141+
142+
whereas with a macro it would be:
143+
144+
```
145+
{
146+
"data": {
147+
"segment": sort(.payload, get-key($ranking, .tag))[0]
148+
}
149+
}
150+
```

functions.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,21 @@ if it is a string that cannot be parsed, then the `fallback` value is
152152
returned.
153153

154154
The number format supported is the same as in JSON literals, except
155-
that leading zeroes are allowed.
155+
that leading zeroes are allowed, and it's allowed to omit the zero
156+
before the period.
156157

157158
Examples:
158159

159160
```
160-
number(23) => 23
161-
number("23") => 23
162-
number("023") => 23
163-
number(23.0) => 23.0
164-
number(null) => null
165-
number("ab") => error
161+
number(23) => 23
162+
number("23") => 23
163+
number("023") => 23
164+
number(23.0) => 23.0
165+
number(".23") => 0.23
166+
number("-.23") => -0.23
167+
number(null) => null
168+
number("ab") => error
169+
number("ab", 0) => 0
166170
```
167171

168172
### _round(float) -> integer_

0 commit comments

Comments
 (0)