---
title: Map
slug: v4-4/XFF0BRYzJaqrHl17qku2k
docTags: 
createdAt: 2023-10-05T15:11:08.674Z
---

With conflict-free replicated data type (CRDT) technology, each document is represented as a `map`. A `map` is useful when you want to create a list of items and update items over time within a document.&#x20;

# Basic Structure

A `map` is a JSON-like object that serves as the basis of each Ditto document and is structured as a collection of field-value pairs:

- To represent simple values in a `map`, use any primitive data type, such as a `string`, `boolean`, `number`, and so on.

- To represent a highly-complex data structure in a `map`, use `register`, `counter`, `array`, or embed another `map`. Embedding a `map` within another map establishes an additional hierarchy.&#x20;

The following snippet demonstrates a Ditto document with an embedded `map`:

:::CodeblockTabs
JSON

```json
{
  "_id": "123546"
  "boolean": true, 
  "string": "Hello World",
  "number": 10,
  "map": {
    "key": "value",
  }
}
```
:::

# Characteristics and Behaviors

Following are the key attributes of the `map` type:

- A `map` is represented in the document as a tree-like structure that establishes a hierarchical, parent-child relationship between the dataset in the document. 

- Use `maps` in scenarios where you want to create a list of items and update that list over time.&#x20;

- If one peer creates a field within a document as an Array and another peer an object, such as a `map` or `register`, the values do not merge.&#x20;

# Embedding a Map

To create a single `map` represented as a JSON-like root object in the document, use the following data model:

:::CodeblockTabs
```swift
do {
    // Insert JSON-compatible data into Ditto
    try ditto.store["foo"].upsert([
        "boolean": true,
        "string": "Hello World",
        "number": 10,
        "map": ["key": "value"],
        "array": [1,2,3],
        "null": nil
    ])
}
catch {
    //handle error
    print(error)
}
```

```kotlin
ditto.store["foo"].upsert(mapOf(
    "boolean" to true,
    "string" to "Hello World",
    "number" to 10,
    "map" to mapOf("key" to "value"),
    "array" to listOf(1,2,3),
    "null" to null
))
```

```javascript
// Insert JSON-compatible data into Ditto
await ditto.store.collection('people').upsert({
  boolean: true,
  string: 'Hello World',
  number: 10,
  map: { key: 'value' },
  array: [],
  null: null,
})
```

```java
// Insert JSON-compatible data into Ditto
Map<String, Object> content = new HashMap<>();
content.put("boolean", true);
content.put("string", "Hello World");
content.put("number", 10);

Map<String, String> innerMap = new HashMap<>();
innerMap.put("key", "value");
content.put("map", innerMap);
content.put("array", Arrays.asList(1, 2, 3));
content.put("null", null);
ditto.store.collection("foo").upsert(content);
```

```csharp
var content = new Dictionary<string, object>
{
    { "boolean", true },
    { "string", "Hello World" },
    { "number", 10 },
    { "map", new Dictionary<string, string>{{ "key", "value"}} },
    { "array", new[] {1, 2, 3} },
    { "null", null }
};
Ditto.Store.Collection("foo").Upsert(content);
```

```cpp
// Insert JSON-compatible data into Ditto
ditto.get_store().collection("foo").upsert(json({{"boolean", true},
                                                 {"string", "Hello World"},
                                                 {"number", 10},
                                                 {"map", {{"key", "value"}}},
                                                 {"array", {1, 2, 3}},
                                                 {"null", NULL}}));
```

```rust
collection
    .upsert(json!({
      "boolean": true,
      "string": "Hello World",
      "number": 10,
      "map": {
        "key": "value"
      },
      "array": [1,2,3],
      "null": null,
    }))
    .unwrap();
```
:::

If you need to represent and organize data in a hierarchical structure, you can embed a `map` within another `map` to establish a parent-child relationship within a document:

:::hint{type="info"}
Each document in Ditto is inherently a `map` object at its root.&#x20;

That is, when you use the Upsert API to create a new document, a top-level `map` Ditto automatically generates a CRDT `map` at the root of the document. For instance, the `parent` field in the following snippet is actually the document's root `map`. For more information, see *Platform Manual&#x20;*> [CRDT Documents](docId\:utI2PcPnojh74wHSuRD9R).&#x20;
:::

:::CodeblockTabs
JSON

```json
{
  "parent": "Susan",
  "map": {
    "child1": {
      "key1": "value1",
      "key2": "value2"
    },
    "child2": {
      "key1": "value3",
      "key2": "value4"
    }
  }
}
```
:::

# Updating a Map

When updating and adding fields to a `map` embedded within another `map`, use *keypath indexing.&#x20;*&#x41;lso referred to as *dot syntax*, a keypath index concisely specifies the fields to update when working with a `map` embedded within another map. &#x20;

In a keypath index, each dot (`.`) represents a level in the `map` hierarchy. For example, `friends.foo` indicates that the `foo` field is a child of the parent `friends` `map`, as demonstrated in the following snippet. &#x20;

## Preferred: Set Specific Value&#x20;

By calling the Remove method, as follows, you omit only the `foo` field from the `friends` `map` within the document, while the other fields within the `friends` `map` remain unaffected:

:::CodeblockTabs
pseudocode

```javascript
collection.findByID("map_test").update(doc => {
  doc.at("friends.foo").set("bar")
})
```
:::

## Not Recommended: Update Entire Map

The following snippet results in all of the values in the friends map being replaced with the new object: `({ "beep": "boop" }):`

:::CodeblockTabs
pseudocode

```javascript
collection.findByID("map_test").update(doc => {
  doc.at("friends").set({
     "beep": "boop"
  })
})
```
:::

# Removing a Map

Since CRDT `map` values merge with the existing document, simply omitting them from the CRDT `map` does not remove them.&#x20;

Instead, the CRDT `map` creates an operation for that field, and subsequently the existing fields remain unchanged.[​](https://docs.ditto.live/javascript/common/datamodel/map#remove)

## Preferred: Update Specific Value

By calling the Remove method, as follows, you omit only the `foo` field from the `friends` `map` within the document, while the other fields within the `friends` `map` remain unaffected:

:::CodeblockTabs
pseudocode

```javascript
collection.findByID("map_test").update(doc => {
  doc.at("friends.foo").set("bar")
})
```
:::

## Not Recommended: Update Entire Map

When you want to clear the entire `map` structure embedded in the document, call the `set` method.

For example, the following snippet illustrates the process of removing the `friends` `map` through the `set `method, making it empty.&#x20;

:::CodeblockTabs
pseudocode

```javascript
collection.findByID("map_test").update(doc => {
  doc.at("friends").set({
     "beep": "boop"
  })
})
```
:::

# Handling Type-Level Concurrency Conflicts

An issue unique to `maps` is the possibility for two offline peers to create a new document, in which one peer represents the field as an object (`map`), while the other peer represents the field as an `array`.

## Example Scenario: Divergent Types Preventing Merge

The following snippets illustrate a scenario of a type-level conflict unique to `maps`.&#x20;

Peer A creates the following new document:

:::CodeblockTabs
JSON

```json
{
  "name":"Bob Jones",
  "address": {
    "street":"Long Road",
    "house number":10298,
    "zip":"90210"
  }
}
```
:::

While at the same time Peer B creates the following new document:

:::CodeblockTabs
JSON

```json
{
  "name": "Bob Jones",
  "address":[
    10298,
    "Long Road",
    "90210"
  ]
}
```
:::

Because peer A and peer B use divergent data structures, combining an `array` with an object (`MAP`) is impossible.&#x20;

Rather than adhering to the default "last updated type" win principle, which can trigger a *ping-pong* alteration of types between connected peers, as demonstrated in the following snippet, retain both values for the `address` field property by creating a data structure that accommodates both the object (`MAP`) and the `array`.&#x20;

:::hint{type="info"}
A ping-pong alteration of types occurs when distinct peers repeatedly modify the data type of a specific field in response to each other's updates, leading to a forever loop of back‑and‑forth behavior.&#x20;
:::

```json
{
  "name": "Bob Jones",
  "address": {
    "objectVersion": {
      "street": "Long Road",
      "house number": 10298,
      "zip": "90210"
    },
    "arrayVersion": [
      10298,
      "Long Road",
      "90210"
    ]
  }
}
```

## Preventing the Ping-Pong Effect

To avoid the ping-pong effect when conflicts between data types occur, retain both the `array` and the `map` object representations of the field in the document. For example, in the previous scenario, you keep both versions of the conflicting representation of the `address` field in the document.

Retaining both the `array` and `map` representations:

- Prevents back-and-forth, ping-pong behavior

- Ensures that there is no loss of data

- Provides flexibility, allowing you to choose the data type that is most appropriate for encoding data in JSON based on your specific requirements and use case

# Managing Concurrency Conflicts: Update History

The best approach to handle conflicts that result from two peers making concurrent offline edits and then later rejoining online depends on your specific requirements and use case.

Following is an overview of best approaches for handling concurrency conflicts:

- Resolving concurrency conflicts — If you want to give priority to the "latest" change, use a `register`. Ditto's `register` type use Last-Write-Wins semantics so the value written last always becomes the current value.&#x20;

- Auditing concurrency conflicts — If you want to keep track of the changes made by different peers over time, use the `map` type to model your list of operations.&#x20;

  Each write operation is independently tracked as a field-value pair, with the field representing the unique identifier and the value storing only the specific changes made by a given peer.&#x20;

- Prompting end users to choose — If you want your end users to resolve concurrency conflicts instead of Ditto, use the `map` type inside of your document and prompt end users to select the value to replicate.&#x20;

## Example Scenario: Using a Map for Concurrent Updates

Imagine a scenario in which two Ditto stores, peer A and peer B, have the following document:

:::CodeblockTabs
JSON

```json
{
  "_id": "abc123",
  "color": "red",
  "make": "Toyota",
  "mileage": 160000,
  "inspections": "<very large map>"
}
```
:::

Peer A calls the Upsert method to change the field-value `color:red` to `color:blue`:

:::CodeblockTabs
```kotlin
val initialDocument = mapOf(
  "_id" to "abc123",
  "color" to "blue"
)
```

```javascript
upsert({
  _id: "abc123",
  color: "blue"
})
```
:::

While at the same time peer B calls the Update method to change the value of the `mileage` field:&#x20;

:::CodeblockTabs
```kotlin
findById("abc123").update(doc => {
  doc.mileage.incrememt(200)
})
ditto.store.collection("cars").update(initialDocument)
```

```javascript
findById("abc123").update(doc => {
  doc.mileage.incrememt(200)
})
```
:::

When the changes replicate across the distributed peers, both changes merge resulting in both peer A and peer B Ditto stores having the mileage `increment` of `200` and the `color` change to `blue`:&#x20;

:::CodeblockTabs
JSON

```json
{
  "_id": "abc123",
  "color": "blue",
  "make": "Toyota",
  "mileage": 160200,
  "smogReports": "<very long json blob>"
}
```
:::

