---
slug: /experiments/map-variables/
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Map Variables (#1585)

:::caution

All experimental features are subject to breaking changes and/or removal _at any
time_. We strongly recommend that you do not use these features in a production
environment. They are intended for testing and feedback only.

:::

Currently, Task supports all variable types except for maps. This experiment
adds two different proposals for map variables. Click on the tabs below to
switch between them.

<Tabs defaultValue="1" queryString="proposal"
  values={[
    {label: 'Proposal 1', value: '1'},
    {label: 'Proposal 2', value: '2'}
  ]}>

<TabItem value="1">

:::warning

This experiment proposal breaks the following functionality:

- Dynamically defined variables (using the `sh` keyword)

:::

:::info

To enable this experiment, set the environment variable:
`TASK_X_MAP_VARIABLES=1`. Check out [our guide to enabling experiments
][enabling-experiments] for more information.

:::

This proposal removes support for the `sh` and `ref` keywords in favour of a new
syntax for dynamically defined variables and references. This allows you to
define a map directly as you would for any other type:

```yaml
version: 3

tasks:
  foo:
    vars:
      FOO: {a: 1, b: 2, c: 3} # <-- Directly defined map on the `FOO` key
    cmds:
      - 'echo {{.FOO.a}}'
```

## Migration

Taskfiles with dynamically defined variables via the `sh` subkey or references
defined with `ref` will no longer work with this experiment enabled. In order to
keep using these features, you will need to migrate your Taskfile to use the new
syntax.

### Dynamic Variables

Previously, you had to define dynamic variables using the `sh` subkey. With this
experiment enabled, you will need to remove the `sh` subkey and define your
command as a string that begins with a `$`. This will instruct Task to interpret
the string as a command instead of a literal value and the variable will be
populated with the output of the command. For example:

<Tabs defaultValue="2"
  values={[
    {label: 'Before', value: '1'},
    {label: 'After', value: '2'}
  ]}>

<TabItem value="1">

```yaml
version: 3

tasks:
  foo:
    vars:
      CALCULATED_VAR:
        sh: 'echo hello'
    cmds:
      - 'echo {{.CALCULATED_VAR}}'
```

</TabItem>
<TabItem value="2">

```yaml
version: 3

tasks:
  foo:
    vars:
      CALCULATED_VAR: '$echo hello' # <-- Prefix dynamic variable with a `$`
    cmds:
      - 'echo {{.CALCULATED_VAR}}'
```

</TabItem></Tabs>

### References

<Tabs defaultValue="2"
  values={[
    {label: 'Before', value: '1'},
    {label: 'After', value: '2'}
  ]}>

<TabItem value="1">

```yaml
version: 3

tasks:
  foo:
    vars:
      VAR: 42
      VAR_REF:
        ref: '.FOO'
    cmds:
      - 'echo {{.VAR_REF}}'
```

</TabItem>
<TabItem value="2">

```yaml
version: 3

tasks:
  foo:
    vars:
      VAR: 42
      VAR_REF: '#.FOO' # <-- Prefix reference with a `#`
    cmds:
      - 'echo {{.VAR_REF}}'
```

</TabItem></Tabs>

If your current Taskfile contains a string variable that begins with a `$` or a
`#`, you will now need to escape it with a backslash (`\`) to stop Task from
interpreting it as a command or reference.

</TabItem>
<TabItem value="2">

:::info

To enable this experiment, set the environment variable:
`TASK_X_MAP_VARIABLES=2`. Check out [our guide to enabling experiments
][enabling-experiments] for more information.

:::

This proposal maintains backwards-compatibility and the `sh` subkey and adds
another new `map` subkey for defining map variables:

```yaml
version: 3

tasks:
  foo:
    vars:
      FOO:
        map: {a: 1, b: 2, c: 3} # <-- Defined using the `map' subkey instead of directly on 'FOO'
      BAR: true # <-- Other types of variables are still defined directly on the key
      BAZ:
        sh: 'echo Hello Task' # <-- The `sh` subkey is still supported
      QUX:
        ref: '.BAZ' # <-- The `ref` subkey is still supported
    cmds:
      - 'echo {{.FOO.a}}'
```

</TabItem></Tabs>

## Looping over maps

This experiment also adds support for looping over maps using the `for` keyword,
just like arrays. In addition to the `{{.ITEM}}` variable being populated when
looping over a map, we also make an additional `{{.KEY}}` variable available
that holds the string value of the map key.

<Tabs defaultValue="1" queryString="proposal"
  values={[
    {label: 'Proposal 1', value: '1'},
    {label: 'Proposal 2', value: '2'}
  ]}>

<TabItem value="1">

```yaml
version: 3

tasks:
  foo:
    vars:
      MAP: {a: 1, b: 2, c: 3}
    cmds:
      - for:
          var: MAP
        cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
```

</TabItem>
<TabItem value="2">

```yaml
version: 3

tasks:
  foo:
    vars:
      map:
        MAP: {a: 1, b: 2, c: 3}
    cmds:
      - for:
          var: MAP
        cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
```

:::note

Remember that maps are unordered, so
the order in which the items are looped over is random.

:::

</TabItem></Tabs>

{/* prettier-ignore-start */}
[enabling-experiments]: ./experiments.mdx#enabling-experiments
{/* prettier-ignore-end */}