JavaScript: Building Table Sorting and Pagination

As part of my job in managing this blog, I check my stats frequently, and I've noticed that some of my more basic Vue.js articles have had consistently good traffic for quite some time. As I find myself doing more and more with "regular" JavaScript (sometimes referred to as "Vanilla JavaScript", but I'm not a fan of the term) I thought it would be a good idea to update those old posts for folks who would rather skip using a framework. With that in mind, here is my update to my post from over four years ago, Building Table Sorting and Pagination in Vue.js

You don't need to read that old post as I think the title is rather descriptive. How can we use JavaScript to take a set of data - render it in a table - and support both sorting and pagination? Here's how I solved this. Before I begin, a quick note. I'll be loading all of my data and while that works with a "sensible" amount of data, you don't want to be sending hundreds of thousands of rows of data to the client and sorting and paging in the client. That being said, I think an entirely client-side solution is absolutely safe if you understand the size of your data and know it's not going to impact performance.

Grouping and Sorting Records in Kumologica

Grouping and sorting of records are common functionalities seen in many microservices and integration requirements. In most use cases, the records fetched from the source system might be in raw form, with all the records treated separately, without having any records grouped as per the functional requirements. Similarly, another functionality that is popularly used is the sorting of records. This can be either sorting based on an existing property in the record, or it can be based on the computation of some properties and then sorting the records based on the result of the computation.

Grouping

Grouping is the functionality of combining records with a common property or attribute as a single unit. This can be based on one property or multiple properties associated with the records. These properties can also be referred to as keys. In the diagram below, we can see cars of different brands grouped based on the their color and type properties. Group 1 has both Ford and BMW since they have the same traits of color and type.

Scala: Sorting Lists of Objects

One of the most common ADT that a developer uses in their day-to-day coding is List. And one of the most common operations a developer performs on a list is to order it or sort it with given criteria. In this article, I will focus on sorting a list of objects in Scala.

Mainly, there are two ways of sorting a list in Scala, i.e.

Level up your .sort game

Sorting is a super handy JavaScript method that can display the values of an array in a certain order. Whether that’s real estate listings by price, burger joints by distance, or best nearby happy hours by rating, sorting arrays of information is a common need.

If you’re already doing this with JavaScript on a project, you are will likely using the built-in array .sort method, which is in the same family of array methods that includes .filter, .map and .reduce.

Let's take a look at how to do that!

A quick note about side effects

Before going into the details of how to use .sort, there is a very important detail that needs to be addressed. While many of the ES5 array methods such as .filter, .map, and .reduce will return a new array and leave the original untouched, .sort will sort the array in place. If this is unwanted, an ES6 technique to avoid this is using the spread operator to concisely create a new array.

const foo = ['c','b','a'];
const bar = ['x','z','y'];
const fooSorted = foo.sort();
const barSorted = [...bar].sort();

console.log({foo, fooSorted, bar, barSorted});

/*
{
  "foo":       [ "a", "b", "c" ],
  "fooSorted": [ "a", "b", "c" ],
  "bar":       [ "x", "z", "y" ],
  "barSorted": [ "x", "y", "z" ]
}
*/

foo and fooSorted both reference the same array, but bar and barSorted are now individual arrays.

General overview

The only parameter of the .sort method is a function. The spec refers to this as the compareFn — I will refer to it as the "comparison function" for the rest of the post. This comparison function accepts two parameters, which I will refer to as a and b. a and b are the two elements that we will be comparing. If you do not provide a comparison function, the array will coerce each element into a string and sort according to Unicode points.

If you would like the a to be ordered first in the array, the comparison function should return a negative integer; for b, a positive integer. If you would like the two to maintain their current order, return a 0.

If you don't understand, don't worry! Hopefully it will become much more clear with a few examples.

Comparing numbers

One of the simplest callbacks to write is a number comparison.

const numbers = [13,8,2,21,5,1,3,1];
const byValue = (a,b) => a - b;
const sorted = [...numbers].sort(byValue);
console.log(sorted); // [1,1,2,3,5,8,13,21]

If a is greater than b, a - b will return a positive number, so b will be sorted first.

Comparing strings

When comparing strings, the > and < operators will compare values based on each string’s Unicode value. This means that all uppercase letters will be “less” than all lowercase letters, which can lead to unexpected behavior.

JavaScript does have a method to help with comparing strings: the String.prototype.localeCompare method. This method accepts a comparison string, a locale, and an options object. The options object accepts a few properties (all of which you can view here), but I find that the most useful is "sensitivity." This will affect how comparisons work between letter variations such as case and accent.

const strings = ['Über', 'alpha', 'Zeal', 'über', 'uber', 'Uber', 'Alpha', 'zeal'];

const sortBySensitivity = sensitivity => (a, b) => a.localeCompare(
  b,
  undefined, // locale string -- undefined means to use browser default
  { sensitivity }
);

const byAccent  = sortBySensitivity('accent');
const byBase    = sortBySensitivity('base');
const byCase    = sortBySensitivity('case');
const byVariant = sortBySensitivity('variant'); // default

const accentSorted  = [...strings].sort(byAccent);
const baseSorted    = [...strings].sort(byBase);
const caseSorted    = [...strings].sort(byCase);
const variantSorted = [...strings].sort(byVariant);

console.log({accentSorted, baseSorted, caseSorted, variantSorted});

/*
{
  "accentSorted":  [ "alpha", "Alpha", "uber", "Uber", "Über", "über", "Zeal", "zeal" ],
  "baseSorted":    [ "alpha", "Alpha", "Über", "über", "uber", "Uber", "Zeal", "zeal" ],
  "caseSorted":    [ "alpha", "Alpha", "über", "uber", "Über", "Uber", "zeal", "Zeal" ],
  "variantSorted": [ "alpha", "Alpha", "uber", "Uber", "über", "Über", "zeal", "Zeal" ]
}
*/

To me, baseSorted seems to be the most logical for most alphabetical sorting — ‘ü’, ‘u’, ‘Ü’, and ‘U’ are equivalent, so they remain in the order of the original array.

Running functions before comparing values

You may want to run a comparison function on a value that is derived from each array’s element. First, let’s write a comparison function factory that will "map" over the element before calling the comparison function.

const sortByMapped = (map,compareFn) => (a,b) => compareFn(map(a),map(b));

One use case for this is sorting based on the attribute of an object.

const purchases = [
  { name: 'Popcorn', price: 5.75 }, 
  { name: 'Movie Ticket', price: 12 },
  { name: 'Soda', price: 3.75 },
  { name: 'Candy', price: 5 },
];

const sortByMapped = (map,compareFn) => (a,b) => compareFn(map(a),map(b));
const byValue = (a,b) => a - b;
const toPrice = e => e.price;
const byPrice = sortByMapped(toPrice,byValue);

console.log([...purchases].sort(byPrice));

/*
[
  { name: "Soda", price: 3.75 },
  { name: "Candy", price: 5 },
  { name: "Popcorn", price: 5.75 },
  { name: "Movie Ticket", price: 12 }
]
*/

Another case might be to compare an array of dates.

const dates  = ['2018-12-10', '1991-02-10', '2015-10-07', '1990-01-11'];
const sortByMapped = (map,compareFn) => (a,b) => compareFn(map(a),map(b));
const toDate = e => new Date(e).getTime();
const byValue = (a,b) => a - b;
const byDate = sortByMapped(toDate,byValue);

console.log([...dates].sort(byDate));
// ["1990-01-11", "1991-02-10", "2015-10-07", "2018-12-10"]

Reversing a sort

There are some cases where you may want to reverse the outcome of a comparison function. This is subtly different than doing a sort and then reversing the result in the way ties are handled: if you reverse the outcome, ties will also be reversed in order.

To write a higher order function that accepts a comparison function and returns a new one, you will need to flip the sign of the comparison’s return value.

const flipComparison = fn => (a,b) => -fn(a,b);
const byAlpha = (a,b) => a.localeCompare(b, null, { sensitivity: 'base' });
const byReverseAlpha = flipComparison(byAlpha);

console.log(['A', 'B', 'C'].sort(byReverseAlpha)); // ['C','B','A']

Running a tiebreaker sort

There are times when you may want to have a "tie-breaker" sort — that is, another comparison function that is used in the case of a tie.

By using [].reduce, you can flatten an array of comparison functions into a single one.

const sortByMapped = map => compareFn => (a,b) => compareFn(map(a),map(b));
const flipComparison = fn => (a,b) => -fn(a,b);
const byValue = (a,b) => a - b;

const byPrice = sortByMapped(e => e.price)(byValue);
const byRating = sortByMapped(e => e.rating)(flipComparison(byValue));

const sortByFlattened = fns => (a,b) => 
  fns.reduce((acc, fn) => acc || fn(a,b), 0);

const byPriceRating = sortByFlattened([byPrice,byRating]);

const restaurants = [
  { name: "Foo's Burger Stand", price: 1, rating: 3 },
  { name: "The Tapas Bar", price: 3, rating: 4 },
  { name: "Baz Pizza", price: 3, rating: 2 },
  { name: "Amazing Deal", price: 1, rating: 5 },
  { name: "Overpriced", price: 5, rating: 1 }, 
];

console.log(restaurants.sort(byPriceRating));

/*
{name: "Amazing Deal", price: 1, rating: 5}
{name: "Foo's Burger Stand", price: 1, rating: 3}
{name: "The Tapas Bar", price: 3, rating: 4}
{name: "Baz Pizza", price: 3, rating: 2}
{name: "Overpriced", price: 5, rating: 1}
*/

Writing a random sort

You might want to sort an array "randomly." One technique that I have seen is to use the following function as the comparison function.

const byRandom = () => Math.random() - .5;

Since Math.random() returns a "random" number between 0 and 1, the byRandom function should return a positive number half of the time and a negative number the other half. This seems like it would be a good solution, but unfortunately, since the comparison function is not "consistent" — meaning it may not return the same value when called multiple times with the same values — it may result in some unexpected results.

For example, let's take an array of numbers between 0 and 4. If this byRandom function was truly random, it would be expected that the new index of each number would be spread out equally over enough iterations. The original 0 value would be just as likely to be in index 4 as index 0 in the new array. However, in practice, this function will bias each number to its original position.

See the Pen
Array.sort() Random 👎
by Adam Giese (@AdamGiese)
on CodePen.

The "diagonal" from the top-left will statistically have the greatest value. In an ideal and truly random sort, each table cell would hover around 20%.

The fix for this is to find a way to ensure that the comparison function remains consistent. One way to do this is to map the random value to each array element before the comparison, then map it away after.

const sortByMapped = map => compareFn => (a,b) => compareFn(map(a),map(b));
const values = [0,1,2,3,4,5,6,7,8,9];
const withRandom = (e) => ({ random: Math.random(), original: e });
const toOriginal = ({original}) => original;
const toRandom = ({random}) => random;
const byValue = (a,b) => a - b;
const byRandom = sortByMapped(toRandom)(byValue);

const shuffleArray = array => array
  .map(withRandom)
  .sort(byRandom)
  .map(toOriginal);

This ensures that each element has a single random value that is only calculated once per element rather than once per comparison. This removes the sorting bias towards the original position.

See the Pen
Array.sort() Random 👍
by Adam Giese (@AdamGiese)
on CodePen.

The post Level up your .sort game appeared first on CSS-Tricks.

Java 8 Comparator: How to Sort a List

In this article, we’re going to see several examples on how to sort a List in Java 8.

Sort a List of Strings Alphabetically

List<String> cities = Arrays.asList(
       "Milan",
       "london",
       "San Francisco",
       "Tokyo",
       "New Delhi"
);
System.out.println(cities);
//[Milan, london, San Francisco, Tokyo, New Delhi]

cities.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println(cities);
//[london, Milan, New Delhi, San Francisco, Tokyo]

cities.sort(Comparator.naturalOrder());
System.out.println(cities);
//[Milan, New Delhi, San Francisco, Tokyo, london]

We’ve written London with a lowercase "L" to better highlight differences between Comparator.naturalOrder(), which returns a Comparator that sorts by placing capital letters first, and String.CASE_INSENSITIVE_ORDER, which returns a case-insensitive Comparator.