demo D26
IP addresses and CIDR
IPv4, IPv6 and subnets that sort correctly
type: 'ipv4' | 'ipv6' | 'cidr'
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.13.0/lattice-grid.min.css">
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.13.0/lattice-grid.min.js"></script>
<div id="grid" style="height: 540px"></div>
<script>
const grid = LatticeGrid.createGrid(document.getElementById('grid'), {
rowKey: 'id',
toolPanel: {
side: 'left',
panels: ['columns', 'filters', 'views', 'quick'],
actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
exportName: 'lattice-demo',
},
columns: [
{ field: 'hostname', title: 'Hostname', layout: { width: 280, pin: 'start' } },
{ field: 'address', title: 'IPv4', type: 'ipv4', layout: { width: 150 } },
{
id: 'stored', title: 'Stored as', type: 'number', layout: { width: 150 },
// The proof, rather than the claim: an ipv4 cell holds a 32-bit
// integer. 10.0.0.5 is 167772165, which is what makes the ordering
// exact rather than lexical.
value: {
deps: ['address'], pure: true,
compute: (d) => {
const parts = String(d.address ?? '').split('.');
if (parts.length !== 4) return null;
return ((+parts[0] * 256 + +parts[1]) * 256 + +parts[2]) * 256 + +parts[3];
},
},
},
{
field: 'address', id: 'astext', title: 'IPv4, as text', type: 'text', layout: { width: 150 },
// The same addresses with no type. Sort both and read the first
// octet down each: the typed column crosses 128.0.0.0 in order.
},
{ field: 'subnet', title: 'Subnet', type: 'cidr', layout: { width: 160 } },
{ field: 'gateway', title: 'Gateway', type: 'ipv4', layout: { width: 150 } },
// Sorted on the eight expanded groups, so ::1 leads and the compressed
// and uncompressed spellings of one address land together.
{ field: 'address6', title: 'IPv6', type: 'ipv6', layout: { width: 250 } },
{
field: 'site', title: 'Site', type: 'lookup', layout: { width: 170 },
lookup: { options: SITES, unknownLabel: 'decommissioned' },
},
],
state: { sort: [{ col: 'address', dir: 'asc' }] },
rows, // nodes, one address per row
});
</script>
Sorting and filtering IPv4, IPv6 and CIDR values correctly
Network data does not sort as text: 10.0.0.2 and 10.0.0.10 need 10.0.0.2 first, and a lexical comparison gets that wrong the moment the octets differ in width. Lattice Grid gives a column its own comparator for this with type: 'ipv4' | 'ipv6' | 'cidr', which parses each value into its numeric form before comparing, so a column of addresses or subnets sorts in true network order regardless of how the strings are padded or written. This is the type to reach for on an inventory of hosts, a firewall rule table, or any dataset from a JavaScript data grid managing infrastructure records where addresses and subnet ranges appear as plain columns rather than free text.
Filtering follows the same parsed representation: a cidr column supports range and containment checks against a subnet, not just string matching, so filtering for “everything in 10.0.0.0/8” behaves as a network engineer expects rather than as a substring search. IPv6 values are normalised before comparison as well, so a compressed form (::1) and an expanded one describing the same address sort and filter identically. Parsing happens once per cell on ingest and the numeric form is what the grid keeps for sorting and filtering, so scrolling or re-sorting a column of addresses costs no more than any other typed column, even at a few hundred thousand rows.
How do you sort IP addresses correctly in a JavaScript data grid?
Set the column’s type to 'ipv4', 'ipv6' or 'cidr' rather than leaving it as text. Lattice Grid parses each value into its numeric form before comparing, so addresses sort in true network order, and subnet values support range and containment filtering against a CIDR block rather than plain string matching.