Re: Using map: filter on array of Products to access a metafield value

Solved

Using map: filter on array of Products to access a metafield value

ri31
Shopify Partner
37 2 13

I want to create a list of all values of a metafield that products in a collection have. For this particular example, the metafield is named Model. I am trying to make a list of all the Models that are in this collection.

 

Based on Shopify Liquid documentation of arrays (https://shopify.dev/docs/api/liquid/filters/array-filters), it should be possible to use the filter map to get this with one line of code, like this:


{%- assign models_list = collection.products | map: 'metafields.custom.model' -%}

<p>{{ models_list | join: ', ' }}</p>

 

However, that is not working. I am getting a list of spaces separated by commas instead of the values of the metafield.

 

Using a for loop I can get the values successfully:

 

{%- assign models_list = "placeholder,placeholder" | split: "," -%}

{%- for product in collection.products -%}
    {% assign temp_array = product.metafields.custom.model | append: ",placeholder" | split: ","%}
    {% assign models_list = models_list | concat: temp_array | uniq %}
    <p>Models List: {{models_list}}</p>
{%- endfor -%}

{% assign models_list = models_list | reject: 'placeholder' %}

<h3> Models List:{{ models_list | join: ', ' }}</h3>
 
Why isn't the map: working?
If my post is helpful, hit Like to help others find a solution.
Accepted Solution (1)
tim
Shopify Partner
4455 529 1628

This is an accepted solution.

 

However, you can chain several maps and it will work, not sure if it will be more efficient then a loop though:

{% assign list = collection.products | map: 'metafields' | map: 'custom' | map: 'model' %}

 

Still,  keep in mind that big arrays like collection.products are paginated in Shopify, so you would not be accessing all collection products this way -- only the first 50-ish products.

If my post is helpful, hit the thumb up button -- it will help others with similar problem to find a solution.
I can be reached via e-mail tairli@yahoo.com

View solution in original post

Replies 7 (7)

tim
Shopify Partner
4455 529 1628

If I remember properly, you can't use nested properties in map, so this would not work

collection.products | map: 'metafields.custom.model' 

while this will:

collection.products | map: 'price' 

 Also, keep int mind that big arrays like collection.products are paginated in Shopify, so you would only be accessing first 50'ish products.

If my post is helpful, hit the thumb up button -- it will help others with similar problem to find a solution.
I can be reached via e-mail tairli@yahoo.com
tim
Shopify Partner
4455 529 1628

This is an accepted solution.

 

However, you can chain several maps and it will work, not sure if it will be more efficient then a loop though:

{% assign list = collection.products | map: 'metafields' | map: 'custom' | map: 'model' %}

 

Still,  keep in mind that big arrays like collection.products are paginated in Shopify, so you would not be accessing all collection products this way -- only the first 50-ish products.

If my post is helpful, hit the thumb up button -- it will help others with similar problem to find a solution.
I can be reached via e-mail tairli@yahoo.com
Xcel_consult
Tourist
5 1 2

Yes, chaining multiple map filters like this:

{% assign list = collection.products | map: 'metafields' | map: 'custom' | map: 'model' %}
can work for extracting nested metafield values, but it’s not necessarily more efficient than a loop.

Key Considerations:
 Pagination Limitation – Shopify limits collection.products to around 50 products per page, so this method won’t capture all products in large collections.

 Handling Empty Values – Some products may not have the model metafield, leading to nil values in the array. Filtering out empty values might be necessary:


{% assign list = list | compact | uniq %}
 Performance – If you have less than 50 products, map chaining is cleaner. But for large collections, a loop with pagination handling is more reliable.

 Best Practice: If working with large collections, use a loop to ensure all products are processed across pages!

ri31
Shopify Partner
37 2 13

Sounds like you just fed Tim's comments to ChatGPT?

If my post is helpful, hit Like to help others find a solution.
ri31
Shopify Partner
37 2 13

Brilliant!

 

I did a quick test as follows and it worked as expected. I added the 'uniq' filter:

{% assign models_list = collection.products | map: 'metafields' | map: 'custom' | map: 'model' | uniq %}

<p>{{models_list | join: ', '}}</p>

 

I checked 3 collections with <50 products and it got all the models each time. I checked on a collection that has more than 50 products and it only got some of the models, as expected, so I can confirm there is a limit here. My collections do mostly have less than 50 parent products (with multiple variants per parent product), so this should work for those cases.

If my post is helpful, hit Like to help others find a solution.

rm-philmells
Shopify Partner
23 0 10

 

Certain array functionalities in Shopify don't support dot notation. Most of the useful functional filters do not support dot notation, although sorting (sort, sort_natural etc.) does support it.

 

It has been suggested previously that this is a limitation of Liquid's implementation of these methods in Ruby.

 

See: https://community.shopify.com/c/technical-q-a/how-can-i-filter-collections-by-metafields-in-liquid/m...

Xcel_consult
Tourist
5 1 2

You're correct that map should work in theory, but the issue likely arises from the way Shopify handles metafields. Here’s why:

Why map Isn't Working:
Metafield Access Issue: Shopify’s map filter works with direct properties of objects but can struggle with nested attributes like metafields. Since metafields.custom.model isn't a direct property of product but a nested attribute, map may return nil or an empty value.

Metafield Data Type: If metafields.custom.model is not a simple string but a json_string or another complex type, map may not extract it correctly.

Empty or Undefined Values: If some products don’t have the model metafield set, map might return an array with empty values, which could explain why you see spaces separated by commas.

Solution:
Since the for-loop method works, you can slightly optimize it while ensuring unique values:

{%- assign models_list = "" -%}

{%- for product in collection.products -%}
{%- if product.metafields.custom.model != blank -%}
{%- assign models_list = models_list | append: product.metafields.custom.model | append: "," -%}
{%- endif -%}
{%- endfor -%}

{%- assign models_list = models_list | split: "," | uniq | sort -%}

<h3>Models List: {{ models_list | join: ', ' }}</h3>
Why This Works:
Checks for blank values to avoid adding empty metafields.

Appends each model to a string before splitting it into an array.

Uses uniq and sort to remove duplicates and organize the list.

Unfortunately, until Shopify enhances Liquid’s handling of metafields, map isn’t always reliable for metafields. There're more information