Skip to main content

Map Functions

Maps are ordered by key when they are returned as values, which makes function output deterministic in tests and API responses. Map functions return null when the input is not a map or a required key argument is not a string.

Overview

GoalFunction
Lookupmap.get, map.has_key
Shape projectionmap.pick, map.keys, map.values, map.entries
Patchingmap.set, map.remove, map.rename, map.merge, map.deep_merge, map.compact
Construct mapsmap.from
Nested map pathsmap.get_path, map.set_path, map.remove_path, map.flatten, map.unflatten
Row-shaped list helpersmap.group_by, map.index_by
Reverse lookupmap.invert
Sizemap.size, value.size

Lookup

Use map.get(map, key[, default]) when a missing key should not be confused with a stored null value. Use map.has_key when presence is the question.

RETURN map.get({name: 'Ada'}, 'name') -- 'Ada'
RETURN map.get({name: 'Ada'}, 'missing') -- null
RETURN map.get({name: 'Ada'}, 'missing', 'n/a') -- 'n/a'

RETURN map.has_key({a: 1, b: null}, 'b') -- true
RETURN map.has_key({a: 1}, 'b') -- false

Dot lookup remains the shortest form when a missing key can simply become null:

WITH {name: 'Ada'} AS person
RETURN person.name

Projection

map.pick(map, keys) keeps only the requested keys that exist. The returned map is still sorted by key in output.

RETURN map.pick({id: 1, name: 'Ada', email: 'ada@example.test'}, ['id', 'name'])
-- {id: 1, name: 'Ada'}

Introspection helpers expose a map as lists:

RETURN map.keys({c: 1, a: 2, b: 3}) -- ['a', 'b', 'c']
RETURN map.values({a: 1, b: 2}) -- [1, 2]
RETURN map.values({a: 1, b: 2}, ['b']) -- [2]
RETURN map.entries({a: 1, b: 2}) -- [['a', 1], ['b', 2]]
RETURN map.size({a: 1, b: 2}) -- 2

Patching

All patching helpers return a new map value; they do not mutate stored properties by themselves. To persist a patched map, assign it in SET.

RETURN map.set({a: 1}, 'b', 2) -- {a: 1, b: 2}
RETURN map.remove({a: 1, b: 2}, 'a') -- {b: 2}
RETURN map.rename({first: 'Ada'}, 'first', 'name')
-- {name: 'Ada'}
RETURN map.compact({a: 1, b: null, c: 3}) -- {a: 1, c: 3}

map.remove also accepts a list of keys:

RETURN map.remove({a: 1, b: 2, c: 3}, ['a', 'c'])
-- {b: 2}

Merge

map.merge(left, right[, strategy]) combines two maps.

StrategyBehaviour
'right'Default. Values from right replace values from left.
'left'Existing values from left win.
'error'Returns null if both maps contain the same key.
RETURN map.merge({a: 1}, {a: 9, b: 2}) -- {a: 9, b: 2}
RETURN map.merge({a: 1}, {a: 9, b: 2}, 'left') -- {a: 1, b: 2}
RETURN map.merge({a: 1}, {a: 9}, 'error') -- null

map.deep_merge(left, right[, strategy]) uses the same conflict strategies, but when both sides contain a map at the same key it merges those nested maps recursively.

RETURN map.deep_merge(
{user: {name: 'Ada', flags: {admin: false}}},
{user: {email: 'ada@example.test', flags: {admin: true}}}
)
-- {user: {email: 'ada@example.test', flags: {admin: true}, name: 'Ada'}}

Use map.merge for flat patches where replacement is exactly what you want. Use map.deep_merge for JSON-like configuration and payload maps where nested keys should survive partial updates.

Constructing Maps

map.from builds a map from alternating key/value lists, pair lists, or parallel key and value lists. Keys must be strings.

RETURN map.from(['a', 1, 'b', 2]) -- {a: 1, b: 2}
RETURN map.from([['a', 1], ['b', 2]]) -- {a: 1, b: 2}
RETURN map.from(['a', 'b'], [1, 2]) -- {a: 1, b: 2}

Flattening

For direct nested lookup and patching, use path helpers. A path can be a dotted string or a list of string keys. Missing reads return null, or the optional default value when one is supplied.

RETURN map.get_path({user: {name: 'Ada'}}, 'user.name')
-- 'Ada'

RETURN map.get_path({user: {name: 'Ada'}}, ['user', 'email'], 'n/a')
-- 'n/a'

RETURN map.set_path({user: {name: 'Ada'}}, 'user.email', 'ada@example.test')
-- {user: {email: 'ada@example.test', name: 'Ada'}}

RETURN map.remove_path({user: {name: 'Ada', email: 'a@x'}}, 'user.email')
-- {user: {name: 'Ada'}}

map.set_path creates missing intermediate maps. If an intermediate path segment exists but is not a map, it is replaced with a map so the requested nested value can be written.

For shape conversion, map.flatten turns nested maps into dotted keys. map.unflatten reverses the operation. Pass a separator string when . is not a good fit for your data.

RETURN map.flatten({user: {name: 'Ada', age: 36}, active: true})
-- {active: true, user.age: 36, user.name: 'Ada'}

RETURN map.unflatten(map.flatten({user: {name: 'Ada'}}))
-- {user: {name: 'Ada'}}

Grouping And Indexing

These helpers operate on a list of maps, which is a common shape after collecting rows or decoding JSON.

WITH [
{id: 'u1', team: 'eng', name: 'Ada'},
{id: 'u2', team: 'eng', name: 'Grace'},
{id: 'u3', team: 'ops', name: 'Lin'}
] AS rows
RETURN map.group_by(rows, 'team') AS by_team,
map.index_by(rows, 'id') AS by_id

map.group_by returns a map from key value to list of matching maps. map.index_by returns a map from key value to the last matching map. Both stringify the grouping value, so integers, booleans, and strings can all be used as group keys.

Inverting

map.invert(map) creates a reverse lookup from each value to its key. Values are stringified, and duplicate stringified values keep the last key in sorted map order.

RETURN map.invert({draft: 0, published: 1})
-- {'0': 'draft', '1': 'published'}

See Also