Group taxonomy terms by first letter of term

Hi all!

Is alphabetical ordering with headings possible—e.g. for a large tags overview?

<h2>3</h2>
<ul>
  <li>3D</li>
</ul>

<h2>A</h2>
<ul>
  <li>Accessibility</li>
  <li>App</li>
</ul>

<h2>H</h2>
<ul>
  <li>Hugo</li>
</ul>

Thank you!

If I’m understanding correctly, you could use GroupBy, then print the first letter of the Key

See https://gohugo.io/templates/taxonomy-templates/#order-alphabetically-example

Also see: https://github.com/gohugoio/hugo/issues/5719 for how we’re going to improve the above in the next Hugo version.

1 Like

Thank you!

I played a bit. Alphabetical ordering is no problem. But it seems quite tricky (for me) to get the first letter of each group of tags–and only if that group exists.

It would be great if there was an out of the box solution, bep.

Okay. Here is a solution that I’ve been able to implement in order to accomplish this:

<!-- Get Page Type -->
{{ $type := .Type }}

<!-- Get Page -->
{{ $page := .Site.GetPage $type }}

<!-- Create an array of alphanumeric characters -->
{{ $symbols := slice "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" }}

<!-- Range through each alphanumeric character -->
{{ range $symbols }}

	<!-- Set the current character in a variable -->
	{{ $symbol := . }}

	<!-- Create a slice to hold this page's taxonomy terms that match the current symbol -->
	{{ $terms := slice }}

	<!-- Range through page's .Data.Terms alphabetically -->
	{{ range $page.Data.Terms.Alphabetical }}

		<!-- Get current term's name -->
		{{ $term := .Name }}

		<!-- If the current term's preFix matches the current iteration of $symbol, then add the term to the $terms slice -->
		{{ if hasPrefix $term $symbol }}
			{{ $terms = $terms | append . }}
		{{ end }}
	{{ end }}

	<!-- Once every term whose preFix matches the current $symbol has been added to the $terms slice, range over the $terms slice. If the $terms slice is greater than 0 (meaning the current symbol has .Data.Terms associated with it, then display a proper heading, and list out each term -->
	{{ if gt $terms 0 }}
		<h2 class="heading">{{ $symbol |upper }}</h2>
		<ul class="term-list">
		{{ range $terms }}
			<li class="term"><a href="{{ .Page.RelPermalink }}">{{ .Page.Name }}</a></li>
		{{ end }}
		</ul>
	{{ else }}

	<!-- Otherwise do nothing ( meaning, any symbol that doesn't have any .Data.Terms associated with it will be skipped ) -->

	{{ end }}
{{ end }}

This is a very strange workaround to accomplish this, but is all I have been able to manage thus far. In practice, I have been able to successfully use this to list out .Data.Terms of a given .Type with terms being arranged under a proper heading, while not listing headings that have no terms associated with them.

I would be more than happy to clarify further, if needed, and welcome, as well, any criticism or critique on the above approach.

3 Likes

Just used this on a project for authors and tags… It work like a charm. Just copy’n’past.
Thanks.

Just wanted to pass on my thanks for this solution. It worked well on separating tags alphabetically on a taxonomy page.

I would use something like this (in a taxonomy template) to support languages that use letters other than a-z, words such as “łękawica” and “Äpfel”.

{{/* Create map where key is the first character and value is a slice of terms. */}}
{{ $m := dict }}
{{ range $k, $_ := .Data.Terms }}
  {{ $firstCharacter := substr $k 0 1 }}
  {{ $s := slice }}
  {{ with index $m $firstCharacter }}
    {{ $s = . | append $k }}
  {{ else }}
    {{ $s = slice $k }}
  {{ end }}
  {{ $m = merge $m (dict $firstCharacter $s) }}
{{ end }}

{{/* List terms grouped by the first character. */}}
{{ range $k, $v := $m }}
  <h2>{{ $k | upper }}</h2>
  <ul>
    {{ range $v }}
      {{ with $.Data.Terms.Get . }}
        <li><a href="{{ .Page.RelPermalink }}">{{ .Page.LinkTitle }}</a></li>
      {{ end }}
    {{ end }}
  </ul>
{{ end }}

This also reduces the number of iterations and eliminates where usage.

1 Like