Sort Posts by Data

I am attempting to sort RegularPages by a json list of permalinks. This json file represents the most viewed posts, so I’d like to retain the order presented in the json file.

I’ve accomplished this, but the method I used seems inefficient. If possible I’d like to cut down my build times as much as possible since I am working with many 100s of posts.

As you will see in my code below, I a nested ranges to accomplish this. My original attempt ranged through RegularPages and just checked if the page RelPermalink was present in the json file - that obviously resulted in a list that was not ordered properly.

Even though I can create this list more effeciently, in a roundabout way, by ranging through the RegularPages, creating a map of RelPermalinks (key) and File.Paths (value) and then ranging through the json data and cross referencing against the map to see if the json permalink is a RegularPage. Then using .GetPage from the .File.Path - that results in a list that I can not paginate.

I am wondering if I am over thinking this and there is a more simple and efficient way to accomplish it.

Any help would be greatly appreciated.

{{ define "main" }}
<div class="container">
    {{ partial "subheader" . }}
    <!--  popular.json contains RelPermalinks for most of the item pages, sorted by views  -->
    <!--  I don't know how to sort RegularPages by an ordered list of RelPermalinks  -->

    <!--  this array will store the pages ordered by the popular.json file  -->
    {{ $popular := slice }}

    <!--  range over the json data  -->
    {{ range  $.Site.Data.popular.feed.entry }}
        <!--  get the RelPermalink from json  -->
        {{ $permalink := index .title "$t" }}
        <!--  range over the item pages -->
        {{ range where $.Site.RegularPages "Type" "items" }}
            <!--  if permalink from json and page permalink match, add page to $popular -->
            <!-- this is being done to retain the proper order of item posts  -->
            {{ if eq $permalink .RelPermalink }}
                {{ $popular = $popular | append . }}
            {{ end }}
        {{ end }}
    {{ end }}
    <!--  range over $popular and paginate  -->
    <div class="item-feed list">
        {{ range (.Paginate $popular).Pages }}
            {{ partial "item" (dict "context" . "type" "list") }}
        {{ end }}
    </div>
    {{ partial "paginator.html" . }}
</div>
{{ end }}

Update

Going about this a slightly different way results in faster builds, but I am still curious if there is a better way than this second attempt, pasted below:

{{ define "main" }}
<div class="container">
    {{ partial "subheader" . }}
    {{ $popular := slice }}
    {{ $items := dict }}
    {{ range where .Site.RegularPages "Type" "items" }}
        {{ $item := dict .RelPermalink .File.Path }}
        {{ $items = merge $items $item }}
    {{ end }}
    {{ range  $.Site.Data.popular.feed.entry }}
        {{ $permalink := index .title "$t" }}
        {{ if isset $items $permalink }}
            {{ $path := index $items $permalink }}
            {{ with $.Site.GetPage $path }}
                {{ $popular = $popular | append . }}
            {{ end }}
        {{ end }}
    {{ end }}
    <div class="item-feed list">
        {{ range (.Paginate $popular).Pages }}
            {{ partial "item" (dict "context" . "type" "list") }}
        {{ end }}
    </div>
    {{ partial "paginator.html" . }}
</div>
{{ end }}

Hi,

I would do it outside of Hugo.

write a script to patch all posts to add weight to frontmatter.

the fastest is

{{ range .Pages.ByWeight }}

In the old days of punch cards we stepped in units of 10 to have have places for insertions :wink:

Hope this helps

Hi @quoid

don’t suppose you saw this post … they have a much shorter template at the end (but there is some grunt work done in python

FYI - I also have read the same approach used by another hugo writer (but they took their post down) - and I have the saved text in local file.

2 Likes

Thank you both for your replies.

@damien1

I did see this and it was what tipped me off to using GA to power my popular section. However when you see his template code, it’s seems like he isn’t ordering the posts by their popularity, but rather shuffling them. The result is that their order is not the same as it is in the json data.

Even if he didn’t shuffle, I believe he would encounter the same issue I did when ranging through RegularPages - the posts would be ordered by their position in the Pages() map, not the json.

For example, if the popular json has the permalinks like:

  • /post/blog-post-02
  • /post/blog-post-03
  • /post/blog-post-01
  • /post/blog-post-04

And you range through the regular pages, using the default ordering

  • /post/blog-post-04
  • /post/blog-post-03
  • /post/blog-post-02
  • /post/blog-post-01

Just checking that the permalink is in the json, will result in an order like the second list, not the first.

@quoid will your .json have post views? the other option I’ve got is to get first 10 by views and shortcode like this …

(assuming your file is data/posts_pageviews.json

<div class="popular-posts">
    {{ range .Site.Data.posts_pageviews }}
    <ol>
        {{ range first 10 . }}
        <li><a href="{{.url}}">{{.title}}</a> {{ .views }} views</li>
        {{ end }}
    </ol>
    {{end }}
</div>

all credit elsewhere … its not my code but if you want the code and article … i can try to email you?

1 Like

@damien1

Thank you again for your reply.

There’s a couple of issues with ranging through the data. The biggest issue for my case is that you can’t paginate.

The second issue in my case, which is solvable (in my updated code) is that the json does not contain the file.path. My post permalinks and filenames differ, so if you want to access post params/data, you need a way to get the file (page) from the permalink (I did it by creating a map with permalink [key] and file.path [value] and then using .GetPage by file.path).

Either way, my update code is much more efficient than the original code and accomplishes what I need it to do, I was mainly curious if there was a better way to accomplish it.

I’ll shoot you an email so you can send the post, if you don’t mind. I am going to mark this solved since the updated code is working fine.

Thank you all, again!

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.