Wednesday, April 22, 2020

How to sort divs based on visitor's country

Recently I've got an opportunity to participate in revamping our company's official website.

One of the suggestions that came up there was to sort the clients' testimonies according to their relevance. And how would you know that you may ask? Well, one way to infer that is by capturing the visitor's geolocation and arranging the testimonials so the closest would show up first. Time to get to work!

To get the visitor's geolocation, initially, I tried the following service: (BTW later I ended up using this instead)

Well, that was the easy part. You just include their script and call a method to get country information.

  <script src=""></script>

The hard part was rather to get the testimonies sorted. Here's what I did.

We have clients from europe, america, middleast and australia. If someone from Norway visits our site, testemonies by Norgegian clients should show up first, then may be Swidish ones and so on.

So it's not just sorting by one country. You need something like "ORDER BY this, this too, this as well" which should work up to many degrees. I was much more interested in writing the sorting algo, overlooking the mechanism to compute the order by sequence. So the order by sequence was merely hardcoded for each potential geographic:
let computeSortOrder = function(countryCode) {
 switch (countryCode) {
  case 'NO':
   return ['NO', 'SE'];
  case 'SE':
   return ['US', 'CA'];

   return [countryCode];

Now we got ourselves an array of counties that we would like our testimonies to be recursively sorted. How would you implement that? Custom sort with recursion!

 1. let sortClients = function(sortOrder, containerId) { 
 2. let divs = $('#' + containerId).find('.testimony');
 3. let orderedDivs = divs.sort(
 4.  function(a, b) {
 6.   let index = 0;
 7.   return innersort();
 8.   function innersort() {
 9.    var matchA = getMatch(a);
10.    var matchB = getMatch(b);
11.    if (matchA && !matchB) {
12.     return -1;
13.    } else if (!matchA && matchB) {.
14.     return 1;
15.    } else if (!matchA && !matchB) {
16.     index++
17.     if (index < sortOrder.length) {
18.      return innersort();
19.     }
20.    }
21.    return 0;
22.   }
23.   function getMatch(item) {
24.    return $(item).data('country') === sortOrder[index];
25.   }
26.  });
27. $('#' + containerId).append(orderedDivs);
30. }
Let me explain. The "sortOrder" is the array of countries returned from my earlier (ugly) function. The "containerId" is the id of the div which encloses all testimonies. Each testimony div is tagged with a class called "testimony".

In line 2, we get hold of all testimony divs we are interested in sorting. From line 3, the custom sort starts.
Basically the sorting function takes two arguments, essentially two adjacent items in the unsorted array say a & b and returns an integer where a negative value means a < b, positive means a > b and 0 means a = b. It is repeated many times for many pairs until all the elements in the array are in sorted order. The algorithm used will depend on your browser's implementation of ECMAScript. Does anyone still remember how bubblesort works? :) Here's some headsup.

So here, the modification to the general sort is we do the matcing recursively for everything in our sortOrder array. In line 15, when we don't find a match for the currently sorted country, we try to find a match for the next potential country in line at the sortOrder array. And it's repeated till the array is exhausted. The beauty is, it's not depending on any fixed degree of sorting. You can pass in an array of all the countries in the world and still get your divs sorted accordingly.

Alright, I hope you got some idea of the code now. By the way, the "data('country')" is how I tagged a testimony to a country in its div. Example below.

<div class="testimony"  data-country='NO'>
 bla bla bla

That's all for now. Stay safe and don't forget to wash your hands! #covid19

Touchpad not working on CloudReady / Chrome OS? Here's how to fix it!

Covid-19 break seems to be opening up interesting avenues for me. I started a storeroom cleanup activity and I found an old laptop which I s...