# group\_by\_property

Groups an array by a property value in a single O(n) pass. Returns an object with `groups` (lookup by key) and `keys` (list of unique keys in order). This is faster than combining `map`, `uniq`, and multiple `where` calls.

```liquid
{% assign grouped = products | group_by_property: "vendor" %}

{% for vendor in grouped.keys %}
  <h2>{{ vendor }} ({{ grouped.groups[vendor].size }} products)</h2>
  {% for product in grouped.groups[vendor] %}
    <p>{{ product.title }}</p>
  {% endfor %}
{% endfor %}
```

#### Syntax

```liquid
{{ array | group_by_property: property }}
{{ array | group_by_property: "nested.property" }}
```

| Parameter  | Description                                                             |
| ---------- | ----------------------------------------------------------------------- |
| `array`    | Array of objects to group                                               |
| `property` | Property name to group by (supports dot notation for nested properties) |

#### Return Value

Returns an object with two properties:

| Property | Type   | Description                                                              |
| -------- | ------ | ------------------------------------------------------------------------ |
| `groups` | Object | Lookup object where keys are property values, values are arrays of items |
| `keys`   | Array  | List of unique property values in the order they were first encountered  |

#### Variants

**group\_by\_property**

Returns `{ groups: { key: [items] }, keys: [unique_keys] }` format.

```liquid
{% assign grouped = orders | group_by_property: "status" %}
{% assign pending = grouped.groups["pending"] %}
{% assign fulfilled = grouped.groups["fulfilled"] %}
```

**group\_by\_fast**

Returns `[{ name, items }, ...]` format (same as native `group_by` but faster).

```liquid
{% assign grouped = orders | group_by_fast: "status" %}
{% for group in grouped %}
  {{ group.name }}: {{ group.items.size }} orders
{% endfor %}
```

#### Examples

**Process products by material (like the import script):**

```liquid
{% comment %} Old way - O(n) + O(n) + O(n*m) {% endcomment %}
{% assign materials = products | map: "material" %}
{% assign unique_materials = materials | uniq %}
{% for material in unique_materials %}
  {% assign group = products | where: "material", material %}
  {% comment %} process group {% endcomment %}
{% endfor %}

{% comment %} New way - single O(n) pass {% endcomment %}
{% assign grouped = products | group_by_property: "material" %}
{% for material in grouped.keys %}
  {% assign group = grouped.groups[material] %}
  {% comment %} process group {% endcomment %}
{% endfor %}
```

**Group GraphQL edges by nested property:**

```liquid
{% assign edges_by_status = result.products.edges | group_by_property: "node.status" %}

{% assign active_products = edges_by_status.groups["ACTIVE"] %}
{% assign draft_products = edges_by_status.groups["DRAFT"] %}
{% assign archived_products = edges_by_status.groups["ARCHIVED"] %}

{% log "Active: " | append: active_products.size %}
{% log "Draft: " | append: draft_products.size %}
{% log "Archived: " | append: archived_products.size %}
```

**Generate report by category:**

```liquid
{% assign orders_by_region = orders | group_by_property: "shipping_address.country" %}

{% log "=== Orders by Region ===" %}
{% for country in orders_by_region.keys %}
  {% assign country_orders = orders_by_region.groups[country] %}
  {% assign total = 0 %}
  {% for order in country_orders %}
    {% assign total = total | plus: order.total_price %}
  {% endfor %}
  {% log country | append: ": " | append: country_orders.size | append: " orders, $" | append: total %}
{% endfor %}
```

**Batch process variants by product:**

```liquid
{% assign variants_by_product = all_variants | group_by_property: "product_id" %}

{% for product_id in variants_by_product.keys %}
  {% assign product_variants = variants_by_product.groups[product_id] %}

  {% comment %} Update all variants for this product in one API call {% endcomment %}
  {% json mutation_input %}
    {
      "productId": "gid://shopify/Product/{{ product_id }}",
      "variants": [
        {% for variant in product_variants %}
          { "id": "{{ variant.id }}", "price": "{{ variant.new_price }}" }{% unless forloop.last %},{% endunless %}
        {% endfor %}
      ]
    }
  {% endjson %}

  {% graphql query: bulk_update_mutation, variables: mutation_input as result %}
{% endfor %}
```

**Using group\_by\_fast for simple iteration:**

```liquid
{% assign grouped = line_items | group_by_fast: "vendor" %}

{% for group in grouped %}
  <div class="vendor-section">
    <h3>{{ group.name }}</h3>
    <ul>
      {% for item in group.items %}
        <li>{{ item.title }} - {{ item.price }}</li>
      {% endfor %}
    </ul>
  </div>
{% endfor %}
```

#### Performance Comparison

For an array of 10,000 products with 500 unique materials:

| Approach                      | Operations                                            |
| ----------------------------- | ----------------------------------------------------- |
| `map` + `uniq` + `where` loop | O(10,000) + O(10,000) + O(10,000 × 500) = \~5,020,000 |
| `group_by_property`           | O(10,000) = 10,000                                    |

**\~500x faster for this use case.**

#### Comparison with group\_by

| Feature            | `group_by`               | `group_by_property` / `group_by_fast`     |
| ------------------ | ------------------------ | ----------------------------------------- |
| Expression support | Yes (`"price > 100"`)    | No, property names only                   |
| Nested properties  | Yes (via expression)     | Yes (via dot notation)                    |
| Performance        | Slower (expression eval) | Faster (direct access)                    |
| Output format      | `[{ name, items }]`      | `{ groups, keys }` or `[{ name, items }]` |

#### Notes

* Supports nested properties using dot notation: `"node.status"`, `"variant.sku"`
* Items with `null` or `undefined` property values are skipped
* Keys are returned in the order they were first encountered
* Returns `{ groups: {}, keys: [] }` for empty or invalid input
* Use `group_by_fast` if you need the same output format as native `group_by`
* Use native `group_by` if you need expression evaluation (e.g., `"price > 100"`)


---

# 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/group_by_property.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.
