Enhancements to the [List.GroupBy:] macro

The [List.GroupBy:] Macro now supports all JSON property types in Normal Mode

We’re excited to announce a significant enhancement to the [List.GroupBy:] macro in ReportMagic. Previously, Normal Mode could only group by string properties - meaning integer fields like severity, boolean fields like cleared, and null values would cause errors.

The Problem

When working with data from APIs like LogicMonitor, properties come in various JSON types. An alert object might look like this:

{
  "id": "LMS123456",
  "severity": 2,
  "cleared": false,
  "acked": false,
  "sdted": false,
  "tenant": "PDL",
  "type": 1,
  "resourceTemplateId": 111613363,
  "resourceTemplateName": "SSL Certificates-",
  "monitorObjectName": "web-server-01",
  "suppressedDescending": null
}

Notice how severity is an integer, cleared is a boolean, and suppressedDescending is null. The old implementation would only works for strings. Grouping by any non-string property would fail.

What’s New

Normal Mode now fully supports grouping by properties of any primitive JSON type:

  • :white_check_mark: Strings - e.g. tenant, monitorObjectName (worked before, still works)
  • :white_check_mark: Integers - e.g. severity, type, resourceTemplateId
  • :white_check_mark: Booleans - e.g. cleared, acked, sdted
  • :white_check_mark: Null values - correctly distinguished from empty strings
  • :white_check_mark: Floats, GUIDs, Dates, TimeSpans, URIs - all supported

Type Preservation in Output

One of the key benefits is that output JSON retains the original property types. Previously, even if grouping had worked, all grouped values would have been converted to strings. Now:

Grouping by integer severity produces:

[
  { "severity": 2, "AlertCount": 270 },
  { "severity": 3, "AlertCount": 28 },
  { "severity": 4, "AlertCount": 2 }
]

Note that severity remains an integer (2, not "2") in the output. This matters when the output feeds into charts, tables, or further macro processing.

Grouping by boolean cleared produces:

[
  { "cleared": true, "AlertCount": 299 },
  { "cleared": false, "AlertCount": 1 }
]

Again, cleared is a proper boolean (true, not "true").

Multi-Property Grouping with Mixed Types

You can now freely combine properties of different types in a single groupBy. For example, grouping by tenant (string) + acked (boolean) + severity (integer):

[
  { "tenant": "PDL", "acked": false, "severity": 2, "AlertCount": 270 },
  { "tenant": "PDL", "acked": false, "severity": 3, "AlertCount": 28 },
  { "tenant": "PDL", "acked": false, "severity": 4, "AlertCount": 2 }
]

Three different types, all preserved correctly in the output.

Null vs Empty String

A subtle but important improvement: null values are now properly distinguished from empty strings. If you group by a property like suppressedDescending that can be either null or a string value, you’ll get separate groups:

[
  { "suppressedDescending": null, "Count": 295 },
  { "suppressedDescending": "some-value", "Count": 5 }
]

Previously this distinction was lost.

Practical Examples

Here are some real-world examples using LogicMonitor alert data. All examples assume you first fetch and parse alerts:

[LogicMonitor.AlertList: deviceGroup=PDL - Panoramic Data/Datacenter, properties=AllAsJson, take=300, =>AlertArray]
[Json.List: value=`{=AlertArray}`, jsonPath="$.[*]", =>Alerts]

Group by integer severity with Count:

[List.GroupBy:
    values=`{=Alerts}`,
    groupByObjectProperties=severity,
    aggregations=id^Count^AlertCount,
    mode=normal,
    =>AlertsBySeverity
]

Group by boolean cleared with Min/Max severity:

[List.GroupBy:
    values=`{=Alerts}`,
    groupByObjectProperties=cleared,
    aggregations=severity^Min^MinSeverity;severity^Max^MaxSeverity;id^Count^AlertCount,
    mode=normal,
    =>SeverityRangeByCleared
]

Mean severity by alert type:

[List.GroupBy:
    values=`{=Alerts}`,
    groupByObjectProperties=type,
    aggregations=severity^Mean^AvgSeverity;severity^Sum^TotalSeverity;id^Count^Count,
    mode=normal,
    =>AvgSeverityByType
]

Comprehensive template analysis:

[List.GroupBy:
    values=`{=Alerts}`,
    groupByObjectProperties=resourceTemplateId,
    aggregations=resourceTemplateName^First^TemplateName;severity^Min^MinSev;severity^Max^MaxSev;severity^Mean^AvgSev;id^Count^AlertCount,
    mode=normal,
    =>TemplateAnalysis
]

This produces a rich dataset showing, for each template, the template name, severity range, average severity, and alert count — all with the resourceTemplateId preserved as a proper integer.

Alert status breakdown with boolean grouping:

[List.GroupBy:
    values=`{=Alerts}`,
    groupByObjectProperties=acked;cleared,
    aggregations=severity^Sum^TotalSeverity;severity^Mean^AvgSeverity;id^Count^Count,
    mode=normal,
    =>SeverityStatsByStatus
]

Three-property grouping (string + boolean + integer):

[List.GroupBy:
    values=`{=Alerts}`,
    groupByObjectProperties=tenant;acked;severity,
    aggregations=id^Count^AlertCount;alertValue^First^SampleAlert,
    mode=normal,
    =>DetailedAlertBreakdown
]

All Available Aggregation Types

As a reminder, all of these aggregation types work with the enhanced grouping:

Aggregation Description
Count Number of non-null items in the group
Sum Sum of numeric values
Min Minimum numeric value
Max Maximum numeric value
Mean Average of numeric values
First First value encountered in the group
Last Last value encountered in the group

Summary

If you’ve been working around the string-only limitation by converting properties to strings before grouping, you can now remove those workarounds. Just point groupByObjectProperties at any property: integer, boolean, null, or string - and it will work correctly with full type preservation in the output.

This enhancement is available in version 4.1.


Have questions or feedback? Reply below or raise an issue via the feedback form in ReportMagic.

1 Like

Great stuff - very useful - thanks! :slight_smile: