# left\_join

Merges two arrays of objects based on matching keys, similar to a SQL LEFT JOIN. Each item in the first array is enriched with properties from the matching item in the second array. Items without a match get `null` values for the second array's properties.

```liquid
{% assign enriched = orders | left_join: customers, "customer_id = id" %}
{% for order in enriched %}
  {% log order.name | append: " - " | append: order.email %}
{% endfor %}
```

#### Syntax

```liquid
{{ array1 | left_join: array2, expression }}
```

| Parameter    | Description                                                                                        |
| ------------ | -------------------------------------------------------------------------------------------------- |
| `array1`     | Primary array (left side) — all items are preserved                                                |
| `array2`     | Secondary array (right side) — properties are merged into matching items                           |
| `expression` | Join condition in the format `"key1 = key2"` where `key1` is from array1 and `key2` is from array2 |

#### Return Value

Returns the first array with properties from the second array merged into each item. Items in the first array that have no match in the second array receive `null` for the second array's properties.

#### Examples

**Enrich orders with customer data:**

```liquid
{% assign enriched_orders = orders | left_join: customers, "customer_id = id" %}
{% for order in enriched_orders %}
  {% log order.name | append: " | Customer: " | append: order.first_name | append: " " | append: order.last_name %}
{% endfor %}
```

**Match import data with existing products by SKU:**

```liquid
{% assign matched = import_rows | left_join: shopify_variants, "sku = sku" %}
{% for row in matched %}
  {% if row.inventory_item_id %}
    {% log "Matched: " | append: row.sku %}
  {% else %}
    {% log "No match: " | append: row.sku %}
  {% endif %}
{% endfor %}
```

**Combine inventory levels with variant data:**

```liquid
{% assign combined = inventory_levels | left_join: variants, "inventory_item_id = inventory_item_id" %}
{% for item in combined %}
  {% log item.sku | append: ": " | append: item.available | append: " in stock" %}
{% endfor %}
```

**Merge CSV data with API results:**

```liquid
{% assign csv_rows = file.content | parse_csv %}
{% assign api_products = result.data.products.edges | map: "node" %}

{% assign merged = csv_rows | left_join: api_products, "handle = handle" %}
{% for row in merged %}
  {% if row.id %}
    {% log "Update: " | append: row.handle %}
  {% else %}
    {% log "Create: " | append: row.handle %}
  {% endif %}
{% endfor %}
```

#### How it works

```
Array 1 (orders):
  [{ id: 1, customer_id: 10 }, { id: 2, customer_id: 20 }, { id: 3, customer_id: 99 }]

Array 2 (customers):
  [{ id: 10, email: "a@test.com" }, { id: 20, email: "b@test.com" }]

Expression: "customer_id = id"

Result:
  [
    { id: 1, customer_id: 10, email: "a@test.com" },
    { id: 2, customer_id: 20, email: "b@test.com" },
    { id: 3, customer_id: 99, email: null }        ← no match, null values
  ]
```

#### Notes

* The expression format is `"key_from_array1 = key_from_array2"` — spaces around `=` are required
* Both inputs must be arrays — throws an error otherwise
* Modifies the first array in place — properties from matching items in the second array are merged directly
* Uses O(n + m) performance via internal Map lookup, not O(n \* m)
* Items without a match receive `null` for all properties from the second array (except the join key)
* If multiple items in the second array share the same key, the last one is used
* See also: `right_join`, `index_by`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.datajet-app.com/liquid/filters/left_join.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
