How to map values in queries for cleaner insights

Sometimes, different values in your dataset actually mean the same thing - at least for your analysis. For instance, you might already know that iOS 19 and iOS 26 are effectively the same iOS version, just built in different development environments. When you run a query, you may want both to appear as a single value. This post shows how to do that with mapping functions in your queries.

Marina Perkunic

Marina supports TelemetryDeck as a consultant for documentation and blog posts
A pie chart that displays different day times

During the beta phase of iOS 26 we noticed iOS reported iOS 19 in some situations for compatibility reasons, and to provide 100% accurate data we had to merge iOS 19 and iOS 26 and treat them both as iOS 26. We are going to use this example to explain how mapping works in TelemetryDeck.

Initial Query

Let’s start with our iOS version example. In our monthly updated surveys, we mapped iOS 19 to iOS 26 because they represent the same OS version. This is normally a rare case, but the perfect example to show where it can be helpful to display two or more related values as one combined value in a TopN Query.

Here’s a standard TopN Query that returns iOS versions from sample data:

iOS versions by week_no mapping

The elements of the query as JSON would look as follows:

{
  "aggregations": [
    {
      "fieldName": "clientUser",
      "name": "users",
      "type": "thetaSketch"
    }
  ],
  "dimension": {
    "dimension": "TelemetryDeck.Device.systemMajorVersion",
    "outputName": "TelemetryDeck.Device.systemMajorVersion",
    "type": "default"
  },
  "filter": {
    "type": "regex",
    "dimension": "TelemetryDeck.Device.systemMajorVersion",
    "pattern": "iOS (.*)"
  },
  "granularity": "week",
  "metric": {
    "metric": "users",
    "type": "numeric"
  },
  "queryType": "topN",
  "threshold": 20
}

Displaying iOS 19 as iOS 26

To display iOS 19 as iOS 26, we’ll replace the standard default dimension definition with an extraction function of type map.

That means in the JSON we have to take out this part:

"dimension": {
    "dimension": "TelemetryDeck.Device.systemMajorVersion",
    "outputName": "TelemetryDeck.Device.systemMajorVersion",
    "type": "default"
  }

And replace it with:

"dimension": {
    "dimension": "TelemetryDeck.Device.systemMajorVersion",
    "extractionFn": {
      "injective": false,
      "lookup": {
        "map": {
          "iOS 19": "iOS 26"
        },
        "type": "map"
      },
      "retainMissingValue": true,
      "type": "lookup"
    },
    "outputName": "TelemetryDeck.Device.systemMajorVersion",
    "outputType": "STRING",
    "type": "extraction"
  }

This snippet tells the query to replace iOS 19 with iOS 26.

  • retainMissingValue: true keeps all values that don’t need mapping (every value that is not iOS 19).
  • injective: false ensures both iOS 19 and iOS 26 are merged and displayed as iOS 26 when they appear together.

Once updated, your query will now return both iOS 19 and iOS 26 under the single label iOS 26.

iOS versions by week_with mapping

Another Example: Grouping Time of Day

This is not limited to iOS versions, of course. Let's create a second case to show the potential of the explained mapping function. For example, you may ask yourself whether a certain event is sent more frequently in the morning or in the evening.

For this, we’ll use the built-in parameter TelemetryDeck.Calendar.hourOfDay, filtered for the event TelemetryDeck.Session.started (you can replace this with any event).

Here’s how you might group the hours:

TimeMapping Name
11:00 PM - 05:59 AMNight
06:00 AM - 11:59 AMMorning
12:00 AM - 02:59 PMMidday / Early Afternoon
03:00 PM - 05:59 PMAfternoon
06:00 PM - 10:59 PMEvening

And the mapping looks like this:

"dimension": {
    "dimension": "TelemetryDeck.Calendar.hourOfDay",
    "extractionFn": {
        "injective": false,
        "lookup": {
            "map": {
                "23": "Night",
                "24": "Night",
                "1": "Night",
                "2": "Night",
                "3": "Night",
                "4": "Night",
                "5": "Night",
                "6": "Morning",
                "7": "Morning",
                "8": "Morning",
                "9": "Morning",
                "10": "Morning",
                "11": "Morning",
                "12": "Midday / Early Afternoon",
                "13": "Midday / Early Afternoon",
                "14": "Midday / Early Afternoon",
                "15": "Afternoon",
                "16": "Afternoon",
                "17": "Afternoon",
                "18": "Evening",
                "19": "Evening",
                "20": "Evening",
                "21": "Evening",
                "22": "Evening"
            },
            "type": "map"
        },
        "retainMissingValue": true,
        "type": "lookup"
    },
    "outputName": "Time of Day",
    "outputType": "STRING",
    "type": "extraction"
}

And that’s it! Your query will now group events by the defined time ranges instead of specific hours.

Times of Day_granularity all


Times of Day_granularity week