javascript
41 lines · 7 steps
Collapsing consecutive log lines in JavaScript
A single pass groups adjacent log entries sharing a key, then formats each run into a compact summary.
Explained by
highlit
1function collapseConsecutiveLogs(lines, { keyFn = (l) => l.message } = {}) {
2 const groups = [];
3
4 for (const line of lines) {
5 const key = keyFn(line);
6 const last = groups[groups.length - 1];
7
8 if (last && last.key === key) {
9 last.count += 1;
10 last.lastSeen = line.timestamp;
11 last.samples.push(line);
12 continue;
13 }
14
15 groups.push({
16 key,
17 level: line.level,
18 message: line.message,
19 count: 1,
20 firstSeen: line.timestamp,
21 lastSeen: line.timestamp,
22 samples: [line],
23 });
24 }
25
26 return groups.map((group) => ({
27 level: group.level,
28 message: group.message,
29 count: group.count,
30 collapsed: group.count > 1,
31 firstSeen: group.firstSeen,
32 lastSeen: group.lastSeen,
33 label:
34 group.count > 1
35 ? `${group.message} (\u00d7${group.count})`
36 : group.message,
37 children: group.count > 1 ? group.samples : undefined,
38 }));
39}
40
41export { collapseConsecutiveLogs };
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Only adjacent items are merged, so identical-but-separated entries stay distinct — order matters.
- 2An injectable keyFn lets callers decide what counts as a duplicate without touching the loop.
- 3Separating accumulation from final formatting keeps each phase simple and testable.
Related explainers
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
javascript
import { useState, useEffect, useCallback, useRef } from 'react'; const cache = new Map(); const inflight = new Map();
Building a stale-while-revalidate hook in React
caching
request-deduplication
custom-hooks
Advanced
10 steps
javascript
import { useEffect, useRef, useState } from 'react'; export function useDelayedFlag(active, delay = 300) { const [visible, setVisible] = useState(false);
Delaying a loading spinner with a React hook
custom-hooks
debouncing
cleanup
Intermediate
8 steps
javascript
const SWIPE_THRESHOLD = 80; const MAX_TRANSLATE = 120; export function attachSwipeToDismiss(element, onDismiss) {
Building a swipe-to-dismiss gesture in JS
touch-events
gesture-detection
dom-manipulation
Intermediate
10 steps
javascript
const { pool } = require('./db'); function withTransaction() { return async (req, res, next) => {
A per-request transaction middleware in Express
middleware
database-transactions
connection-pooling
Advanced
7 steps
javascript
const express = require('express'); const crypto = require('crypto'); const router = express.Router();
Optimistic locking with ETags in Express
optimistic-locking
etag
conditional-requests
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/collapsing-consecutive-log-lines-in-javascript-explained-javascript-fd41/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.