How to filter only top-level pages in category

I have a Jekyll category (NDT). In that category, I have several pages and many sub pages. On the main page for the category (/NDT/index.md), I want to provide links to all the pages, but none of the sub pages. I tried to do this:

<ul>
  {% for page in site.NDT %}
    {% if page.dir == 'NDT' %}
      <li><a href="{{ page.url }}">{{ page.title }}</a></li>
    {% endif %}
  {% endfor %}
</ul>

This didn’t work because the page.dir variable was blank/empty for all my pages.

Thoughts on how I might be able to do this? Note, I don’t want to have to add another bit of metadata to the pages if I can avoid it. If unavoidable, I will do that, but would rather not.

There are several ways you can do this, and I bet you could get fancy with URL filters, but here is the way I know how to do it :slight_smile:

Reference

Before I begin, you should learn the assign tag for creating variables. It is powerful and helps you avoid that nest for/if loop you created by using the where filter, as you will see in my answer below.

My understanding of the question

You have a folder that I will just call docs. Under that folder there are a number of files and folders, like this:

/docs/RootDoc.md
/docs/index.md (or index.html)
/docs/subdocs/SubDoc.md

The RootDoc.md file looks something like this:

---
layout: default
title: Rootdoc
category: NDT
---
This is a page located at the root of the RootDoc folder...

The SubDoc folder looks something like this:

---
layout: page
title: Subdoc
category: NDT
---
This is a page located in the SubDocs folder, underneath RootDoc...

The index page should only display the list of pages where that index file resides.

Option #1: Add more YML front matter (your least preferred option)

Of course, one way to solve this problem is to add some new YAML element(s) to filter the content more easily. I added a new YML front matter item called subfolder and set that to true or false in my simplistic example.

---
layout: page
title: Subdoc
category: NDT
subfolder: false
---
This is a page located in the SubDocs folder, underneath RootDoc...

With that code in place, I can then write code like this:

{% assign ndtPages = site.pages | where: "category", "NDT" | where: "subfolder", false %}
{% for ndtPage in ndtPages %}
    title: {{ ndtPage.title }}
{% endfor %} 

You mention that this method is not ideal because you have to edit many files. Search this site for regex, and you will see several recent posts that cover how to do that global replacement in a folder of files using Visual Studio Code.

Option #2: Count the number of subfolders (proposed solution)

If you want to know where the document is located, you can get the page.url, and it will return something that looks like this:

/docs/rootdoc.html

You can use the Liquid string split filter to turn the URL into an array, breaking up the string using the slash (/). Since there are 2 slashes in my example, the array will be a size of 3.

Here is a document in a subfolder:

/docs/subdocs/subdoc.html

There are 3 slashes in that example, so you get an array size of 4.

Now, let’s use this knowledge to our advantage.

Here is the code I would write to only get a split URL string with an array size of 3 (or 2 slashes):

{% assign ndtPages = site.pages | where: "category", "NDT" %}
{% for ndtPage in ndtPages %}
    {% assign folderCount = ndtPage.url | split: '/' %}
    {% if folderCount.size == 3 %}
      Page title: {{ ndtPage.title }}
      Page folder location: {{ ndtPage.url }}
    {% endif %}
{% endfor %}

First, we use assign to create a collection of pages that contain a category of NDT in the YML front matter.

We then loop through all the pages, but before we do anything, we assign a variable to convert the URL to an array with each item split based on the /.

Now that we have the array, we will only act on the page if the new array has a value of 3 (or two slashes /).

From there, we put whatever content we want on the page, such as displaying the page title and whatever else you want within the if statement.

Considerations

I do not know how deep your folder structure goes, so display the page.url somewhere, count the slashes, and add +1.

Once you get the folder count size right, you will hard-code the folder count into your code. In my case, I have {% if folderCount.size == 3 %}, but yours might be 5 or 8, or whatever. You may also move around the structure of your site.

I recommend you decide how many levels your pages have and add those values to your _config.yml file. For example, you could add the following line anywhere:

# Folder structure+1 for what level of doc to display: 
rootDoc: 3
subDoc: 4

Then, you can replace:

{% if folderCount.size == 3 %}

With:
{% if folderCount.size == site.rootDoc %}

With that consistency in place, if you move your pages around, all you need to do is change the _config.yml file, rather than the logic in all your index pages.

Final note

I will be honest, I could not figure out why split output a 3 instead of a 2 because I rarely use it. Just in case my +1 logic does not work, use this sample code:

{% assign ndtPages = site.pages | where: "category", "NDT" %}
{% for ndtPage in ndtPages %}
    {% assign folderCount = ndtPage.url | split: '/' %}
    Array size: {{ folderCount.size }}
    title: {{ ndtPage.title }}
    folder: {{ ndtPage.url }}
{% endfor %}

That will display all pages with NDT and you can get the Array size number to find out the number to put into your if statement in the solution I provided earlier.

Hope this helps!

Thanks @BillRaymond. The idea to look at the number of folders in the URL was a great and did the job. Now I don’t have to add extra metadata.

Great news! Glad this solution worked for you!