DEV Community

SEN LLC
SEN LLC

Posted on

A Text and JSON Diff Viewer With LCS Algorithm and Semantic Comparison

A Text and JSON Diff Viewer With LCS Algorithm and Semantic Comparison

Two JSON objects with the same keys in different order shouldn't show as "everything changed." This diff tool normalizes JSON (sorted keys, consistent formatting) before comparing, so semantic equivalence produces zero diff. The diff engine itself is an LCS (Longest Common Subsequence) implementation — the same family of algorithms behind git diff.

Comparing text files is a solved problem in the terminal. But when you need to quickly paste two config files or API responses and see what changed, a browser tool with color coding is faster than firing up a terminal.

🔗 Live demo: https://sen.ltd/portfolio/diff-viewer/
📦 GitHub: https://github.com/sen-ltd/diff-viewer

Screenshot

Features:

  • 3 display modes: Unified, Side-by-side, Inline (word-level)
  • JSON-aware: normalizes key order before diffing
  • Stats: additions, deletions, changes
  • Copy diff in unified format
  • Japanese / English, dark / light theme
  • Zero dependencies, 39 tests

The LCS diff algorithm

The core is a standard LCS table between old and new line arrays:

  1. Build an (m+1)×(n+1) table where table[i][j] = length of LCS of old[0..i-1] and new[0..j-1]
  2. Backtrack from table[m][n] to produce edit operations
  3. Equal lines → type: 'equal', only in old → type: 'remove', only in new → type: 'add'

The time complexity is O(mn) where m and n are line counts. For typical config files and API responses (under 1000 lines), this runs in milliseconds.

JSON normalization

The key insight: {"a":1,"b":2} and {"b":2,"a":1} are semantically identical. Before diffing JSON, normalize both sides:

export function normalizeJSON(str) {
  const obj = JSON.parse(str);
  return JSON.stringify(obj, Object.keys(obj).sort(), 2);
}
Enter fullscreen mode Exit fullscreen mode

Object.keys(obj).sort() as the replacer argument sorts keys alphabetically. Combined with indent of 2, both JSON objects produce identical strings — and the diff shows zero changes.

Inline word-level diff

The inline mode shows changes within lines. Instead of marking an entire line as changed, it highlights the specific words that differ:

export function computeInlineDiff(oldStr, newStr) {
  const oldWords = oldStr.split(/(\s+)/);
  const newWords = newStr.split(/(\s+)/);
  // Same LCS algorithm, but on words instead of lines
}
Enter fullscreen mode Exit fullscreen mode

Splitting on (\s+) (with capture group) preserves whitespace in the output, so the reconstructed text has correct spacing.

Tests

39 tests covering identical texts, complete replacement, middle insertion, JSON normalization, inline diff, stats computation, unified format output, and edge cases (empty input, single line, Unicode).

Series

This is entry #39 in my 100+ public portfolio series.

Top comments (0)