Lattice Grid Buy a licence

demo D52

Custom comparators

Sorting by priority words, versions, or a domain rule

value.compare

Building…
Loading a live grid…

The configuration

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/lattice-grid.min.css">

<div id="grid"></div>

<script type="module">
  import React from 'https://esm.sh/react@18';
  import { createRoot } from 'https://esm.sh/react-dom@18/client';
  import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/lattice-grid.esm.min.js';
  import createLatticeGrid from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/modules/react.esm.min.js';

  const LatticeGrid = createLatticeGrid({ React, createGrid });

  // Order by a known ladder rather than by the words themselves: alphabetical
  // would put low above medium.
  const PRIORITIES = ['critical', 'high', 'medium', 'low', 'none'];
  const byPriority = (a, b) => PRIORITIES.indexOf(String(a)) - PRIORITIES.indexOf(String(b));

  // Compare dotted release numbers as numbers, with pre-releases before the
  // release they lead to, so 1.10.0 sorts after 1.9.3 and 2.0.0 after 2.0.0-rc.1.
  const bySemver = (a, b) => {
    const parse = (value) => {
      const [core, pre = null] = String(value == null ? '' : value).split('-');
      return { parts: core.split('.').map((n) => Number(n) || 0), pre };
    };
    const left = parse(a), right = parse(b);
    for (let i = 0; i < 3; i++) {
      const diff = (left.parts[i] || 0) - (right.parts[i] || 0);
      if (diff !== 0) return diff;
    }
    if (left.pre === right.pre) return 0;
    if (left.pre === null) return 1;
    if (right.pre === null) return -1;
    return left.pre < right.pre ? -1 : 1;
  };

  // Keep the unassigned rows at the bottom in both directions. The grid inverts a
  // comparator's result for a descending sort, so a row that must stay put is
  // pushed the other way when the fifth argument says the sort is descending.
  const ownersLast = (a, b, rowA, rowB, descending) => {
    const blankA = a == null || a === '';
    const blankB = b == null || b === '';
    if (blankA && blankB) return 0;
    if (blankA) return descending ? -1 : 1;
    if (blankB) return descending ? 1 : -1;
    return String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0;
  };

  const columns = [
    { field: 'id', title: 'Incident', layout: { width: 130, pin: 'start' } },
    { field: 'priority', title: 'Priority', layout: { width: 120 }, value: { compare: byPriority },
      cell: { decoration: 'pill', variant: { map: { critical: 'danger', high: 'warning', medium: 'info', low: 'neutral', none: 'neutral' } } } },
    { field: 'version', title: 'Version', layout: { width: 150 }, value: { compare: bySemver } },
    { field: 'owner', title: 'Owner', layout: { width: 150 }, value: { compare: ownersLast } },
    { field: 'service', title: 'Service', filter: { type: 'set' } },
    { field: 'summary', title: 'Summary', layout: { flex: 1, min: 240 } },
    { field: 'ageHours', title: 'Age', type: 'number', format: { suffix: ' h' }, layout: { width: 110 } },
  ];

  const rows = [/* incident records */];

  function App() {
    return (
      <LatticeGrid
        rowKey="id"
        columns={columns}
        rows={rows}
        style={{ height: '540px' }}
      />
    );
  }

  createRoot(document.getElementById('grid')).render(<App />);
</script>

Sorting by a custom comparator instead of default ordering

Default sorting compares values with the ordinary less-than rules for numbers and strings, which breaks down as soon as a column carries meaning a plain comparison cannot see: priority words that should read Low, Medium, High rather than alphabetically, semantic version strings where “2.10.0” needs to land after “2.9.0” rather than before it, or a domain-specific rank that only the application knows. A developer reaches for a custom comparator whenever the natural order of a column is not its lexical or numeric order. Lattice Grid exposes this through value.compare, a column option taking a function that receives two cell values and returns the same negative, zero, or positive result a standard JavaScript Array.prototype.sort comparator would, so existing comparator functions for version strings or ranked enums drop in without adaptation. The comparator runs only during a sort operation, not on every render, so attaching one to a column of custom-ranked categories costs nothing while the grid is idle and adds no overhead to scrolling or filtering. Sort state set through a custom comparator is a plain { col, dir } pair like any other sort, so it serialises the same way for a saved view or a shareable link, and the header retains its usual ascending and descending arrow regardless of how the ordering underneath it is computed.

How do I sort a column by custom logic instead of alphabetical or numeric order?

Set value.compare on the column definition to a function taking two cell values and returning negative, zero, or positive, the same contract as a standard array sort comparator. Lattice Grid calls it during the sort pass in place of the default comparison, so a column of priority labels or version strings sorts by the rule the function encodes rather than by character order.