Skip to content

Commit 9c55037

Browse files
committed
fix: Prevent confusing group order for floating points without precision
By imposing an arbitrary 5 decimal places of precision if the 'group by function' expression returns a non-integer numeric value. Fixes #3371
1 parent 0646263 commit 9c55037

File tree

3 files changed

+12
-6
lines changed

3 files changed

+12
-6
lines changed

src/Query/Filter/FunctionField.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,14 @@ export function groupByFunction(task: Task, arg: GroupingArg, queryContext?: Que
283283
return [];
284284
}
285285

286+
if (typeof result === 'number' && !Number.isInteger(result)) {
287+
// Guard against #3371: order of groups, when grouping by "function task.urgency" without specifying precision, is confusing.
288+
// Sorting has been found to be unreliable with varying numbers of decimal places.
289+
// So to ensure consistent sorting, we round the value to a fixed number of decimals and return it as a string.
290+
// This still sorts consistently even when some of the group's values are integers.
291+
return [result.toFixed(5)];
292+
}
293+
286294
// If there was an error in the expression, like it referred to
287295
// an unknown task field, result will be undefined, and the call
288296
// on undefined.toString() will give an exception and a useful error

tests/Query/Filter/FunctionField.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ describe('FunctionField - grouping return types', () => {
522522
['1', ['1']],
523523
['0', ['0']],
524524
['0 || "No value"', ['No value']],
525-
['1.0765456', ['1.0765456']],
525+
['1.0765456', ['1.07655']],
526526
['1.0765456.toFixed(3)', ['1.077']],
527527
['["heading1", "heading2"]', ['heading1', 'heading2']], // return two headings, indicating that this task should be displayed twice, once in each heading
528528
['[1, 2]', ['1', '2']], // return two headings, that need to be converted to strings

tests/Query/Group/TaskGroups.test.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ describe('Grouping tasks', () => {
238238
`);
239239
});
240240

241-
it.failing('sorts raw urgency value groups correctly', () => {
241+
it('sorts raw urgency value groups correctly', () => {
242242
jest.useFakeTimers();
243243
jest.setSystemTime(new Date('2025-03-07'));
244244

@@ -256,10 +256,8 @@ describe('Grouping tasks', () => {
256256
const grouping = [new FunctionField().createGrouperFromLine('group by function task.urgency')!];
257257

258258
const groups: TaskGroups = makeTasksGroups(grouping, tasks);
259-
const groupHeadings = groups.groups.map((group: TaskGroup) => group.groups[0].slice(0, 7));
260-
261-
expect(groupHeadings).toEqual(['14.8357', '15.75', '15.2928']); // This passes: it shows that the order is illogical (non-ascending).
262-
expect(groupHeadings).toEqual(['14.8357', '15.2928', '15.75']); // This fails: it is what users would expect.
259+
const groupHeadings = groups.groups.map((group: TaskGroup) => group.groups[0]);
260+
expect(groupHeadings).toEqual(['14.83571', '15.29286', '15.75000']);
263261
});
264262

265263
it('handles tasks matching multiple groups correctly', () => {

0 commit comments

Comments
 (0)