Filter Product Images array by ALT text keyword

I have an issue that seems to be fairly common, but I’ve not found a poster that is trying to go about solving it in the same way I am. Essentially, my client has a website that has some unisex products. And on the product and collection pages, they want to change the title and images of the product based on whether it is shown in the Men’s or Women’s collection. I’m choosing to tackle it like this:

  1. Assign all unisex products the tag “unisex” plus “men” plus “women” so they’ll be added to a smart collection for Men’s and Women’s and so I can target the unisex products by that tag.

  2. Add the word “Girls” or “Guys” to the ALT text string to hopefully sort them. I choose “girls” and “guys” since “men” is contained in the word “women”.

Now I can check if the product is tagged “unisex” and check if the word “mens” or “womens” appears in the collection URL to know what type of product page I’m on and if it’s an unisex product. And then can easily add the word “Men’s” or “Women’s” to the title. But the images are proving to be a bit tricky. I want to filter the array by only items containing my keywords (“guys” on Men’s collection and “girls” on Women’s). I know I can do a where filter, but that has to be an exact match. As in {% assign uni_media = product.media | where:“alt”, “Guys” %} . And that works, if my ALT text is exactly “Guys”. But I want to let my client write real SEO-friendly ALT tags, like “Guys Black Crew Neck T-Shirt”. So I researched Jekyll and Liquid a bit, and there’s something called where_exp, as in a where expression. So I could write a contains expression. I thought this must be my answer, however it’s not working. It just returns the full array no matter what parameters I use. Does anyone know if where_exp works in Shopify’s Liquid. And if so, maybe take a look at my logic/syntax and see if I’ve made a mistake? Or just give me a better way to go about getting my filtered array if I’m missing something obvious. I’d greatly appreciate it.

Here’s what I’m trying:

{%- if collection.url contains ‘/mens’ and product.tags contains ‘Unisex’ -%}
{%- assign uni_media = product.media | where_exp:“product.media”, “product.media.alt contains ‘Guys’” -%}
{%- elsif collection.url contains ‘/womens’ and product.tags contains ‘Unisex’ %}
{% assign uni_media = product.media | where_exp:“product.media”, “product.media.alt contains ‘Girls’” -%}
{%- else -%}
{%- assign uni_media = product.media | where: “media_type”, “image” -%}
{%- endif -%}

Then later I just use uni_media in place of product.media to get images.

Hi @freehand_sam welcome, please use the code format tag on code samples, and when describing logic try to either use list-bullets or break up text in shorter paragraphs to avoid walls of text when describing logic it can drastically increase the chances of getting third party help.

Does anyone know if where_exp works in Shopify’s Liquid. And if so, maybe take a look

If you do not see it in the liquid docs https://shopify.dev/api/liquid assume it doesn’t exist or at least not officially supported on shopify even if it’s available.

Liquid is an open source general templating language made by Shopify.

The flavor of liquid used on shopify is not 1:1 to what is openly available or in other flavors i.e. Jekyll.

So unless somethings gone undocumented were kinda stuck with a problem and multiple ways to navigate it.


Unfortunately these situations are still an overcomplicated kludge because shopify-liquid has no “filter” array-filter or search-query syntax for the contains keyword nor a filter version of “contains”. But mainly we cannot properly create objects nor properly create arrays containing objects(see example of this kludge at the end and a workaround).

Most involved but cleanest data approach could be to create metafields on the images through the api as a condition to explicitly check but atm I can’t remember if those are usable with the “where” filter. If your not an app dev try usemechanic scripting https://tasks.mechanic.dev/?tags=Metafields ,or an spreadsheet app like matrixify might do it.

Otherwise using only image alt-text in liquid the media array has to be looped over for all the images and use explicit string comparison|contains checks to skip part of a loop

I.e. {% unless image.alt contains “guy” %}{% continue %}{% endunless %}

https://shopify.dev/api/liquid/tags/iteration-tags#continue

Or move calls to product.media items into a snippet that accepts the contexts variables and returns the proper image or not.

Heavier Alternatives:

metafield definition of type file_reference - explicity set the images on those. Con - Are not grouped as a media array so each has to be called separately.

pseudo proxy products - products that explicitly set up to contain specific sets of images. Con - Have to be prevented from being rendered in the theme however.


We cannot build custom objects, or an array of the valid image media objects simply via the array contact filter ; as those images would be object-“strings”|Drops.

Concat will not work because it’s not being given arrays to work.

Notice in the below that the first “custom_product_media_array” ends up being null.

And any “join” or “split” tricks just stringifies the object property.

So we have to have a way to force even and object out of an array to to still be an array.

We can use the uniq filter to do this.

The following it assumes an all collection to find a product and at least 2 images on that product.

Reassign as needed.

{% assign product = collections.all.products[0] %}

Product handle: {{ product.handle }},

{% assign product_media = product.media %}
Product media: {{ product_media }}

,

{% assign product_media_array_first_object = product_media.first %}
{% assign product_media_array_last_object = product.media.last %}

product_media_array_first_object: Size = {{ product_media_array_first_object | size }},  {{ product_media_array_first_object }}

,

product_media_array_last_object: Size = {{ product_media_array_last_object | size }} , {{ product_media_array_last_object }}

,

{% assign custom_product_media_array = product_media_array_first_object| concat: product_media_array_last_object %}
custom_product_media_array???: {{ custom_product_media_array | json }}

,

Object Arrays

{% assign product_media_array_first_object_ARRAY = product_media.first | uniq  %}
{% assign product_media_array_last_object_ARRAY = product.media.last | uniq %}

product_media_array_first_object_ARRAY: Size = {{ product_media_array_first_object_ARRAY.size }}, {{ product_media_array_first_object_ARRAY }}

,

product_media_array_last_object_ARRAY: Size = {{ product_media_array_last_object_ARRAY | size }},  {{ product_media_array_last_object_ARRAY }}

,

{% assign custom_product_media_array_of_objects = product_media_array_first_object_ARRAY| concat: product_media_array_last_object_ARRAY %}

custom_product_media_array_of_objects ???: Size = {{ custom_product_media_array_of_objects | size }} ,  {{ custom_product_media_array_of_objects | json }}

,

 {{ custom_product_media_array_of_objects.last.alt }}

The only other escape hatch here is using the JSON filter but then your having to parse JSON in a template language.

Thanks for taking the time, Paul. Yeah, upon re-reading this morning, that was quite the wall of text. You’ve helped me out a ton. For those trying to do something similar, I basically ended like this:

{% if collection.url contains '/mens' and product.tags contains 'Unisex' %}
{%- for media in product.media -%}
            {%- unless media.alt contains "Girls" -%}
            	//However you're going to show your image
            {%- endunless -%}
          {%- endfor -%}
        
		
          {% elsif collection.url contains '/womens' and product.tags contains 'Unisex' %}
          {%- for media in product.media -%}
            {%- unless media.alt contains "Guys" -%}
            	//However you're going to show your image
            {%- endunless -%}
          {%- endfor -%}
			
          {%- else -%}
          	{%- for media in product.media -%}
            	//However you're going to show your image
          {%- endfor -%}
          
          {% endif %}

Hi Sam,

This looks like a solution to my issue. Where did you copy this code into? Was it base.css

No, this is liquid code, not css. And it went into the snippets/product-grid-item.liquid file - if your theme has that snippet. Basically where you show your collection product grid and its image(s).

FWIW, I just checked the site to refresh my memory and the client seems to have moved on to just creating two separate products for each unisex item - a men’s and a women’s. They aren’t even using this anymore. ¯_(ツ)_/¯

Thanks for the reply! I’ll give it a go in a snippet file, this definitely seems like the right way for us to go. There just isn’t much information anywhere on how to do it and i haven’t got a clue on how to right code!

Ryan.

This won’t work for Dawn, i don’t seem to have that snippet file. Any other solutions you found to this problem?

Ryan.