(str)
| 1354 | }) |
| 1355 | } |
| 1356 | huffmanEncode(str) { |
| 1357 | const tree = createTree(str); |
| 1358 | const codebook = createCodebook(tree); |
| 1359 | return { |
| 1360 | string: [...str].map(c => codebook[c]).join(""), |
| 1361 | tree, |
| 1362 | codebook |
| 1363 | }; |
| 1364 | |
| 1365 | function createTree(str) { |
| 1366 | const chars = [...str]; |
| 1367 | const charCounts = chars.reduce((counts, char) => { |
| 1368 | counts[char] = (counts[char] || 0) + 1; |
| 1369 | return counts; |
| 1370 | }, {}); |
| 1371 | |
| 1372 | const nodes = Object.entries(charCounts).map(([key, weight]) => ({ key, weight })); |
| 1373 | |
| 1374 | // This queue implementation is horribly inefficient, but a proper, heap-based implementation would |
| 1375 | // be longer that the algorithm itself |
| 1376 | function makeQueue(iterable) { |
| 1377 | return { |
| 1378 | data: [...iterable].sort((a, b) => a.weight - b.weight), |
| 1379 | enqueue(value) { |
| 1380 | const target = this.data.findIndex(x => x.weight > value.weight); |
| 1381 | if (target === -1) { |
| 1382 | this.data.push(value); |
| 1383 | } else { |
| 1384 | this.data = [...this.data.slice(0, target), value, ...this.data.slice(target)]; |
| 1385 | } |
| 1386 | }, |
| 1387 | dequeue() { |
| 1388 | return this.data.shift(); |
| 1389 | } |
| 1390 | }; |
| 1391 | } |
| 1392 | |
| 1393 | const priorityQueue = makeQueue(nodes); |
| 1394 | while (priorityQueue.data.length > 1) { |
| 1395 | const left = priorityQueue.dequeue(); |
| 1396 | const right = priorityQueue.dequeue(); |
| 1397 | priorityQueue.enqueue({ weight: left.weight + right.weight, left, right }); |
| 1398 | } |
| 1399 | return priorityQueue.dequeue(); |
| 1400 | } |
| 1401 | |
| 1402 | function createCodebook(tree) { |
| 1403 | return recurse(tree, "", {}); |
| 1404 | |
| 1405 | function recurse(node, bitstring, dict) { |
| 1406 | if (!node.left && !node.right) { |
| 1407 | dict[node.key] = bitstring; |
| 1408 | } else { |
| 1409 | if (node.left) { |
| 1410 | recurse(node.left, `${bitstring}0`, dict); |
| 1411 | } |
| 1412 | |
| 1413 | if (node.right) { |
no test coverage detected