From 20abd1f3fa3e2d56e5bedc9bec99038d97155ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Nikolic=CC=81?= Date: Sun, 19 Jul 2026 01:04:38 +0200 Subject: [PATCH] Reduce redundant SortLab code --- src/App.jsx | 443 ++++++++++++++---------------------- src/main.jsx | 66 +----- src/styles.css | 35 +-- src/utils/arrayInput.js | 8 - src/utils/sortAlgorithms.js | 298 +++++++++++------------- 5 files changed, 310 insertions(+), 540 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 9628927..51b0ebf 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,6 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { formatArrayInput, parseArrayInput } from './utils/arrayInput'; -import { algorithmContent, algorithmMap } from './utils/sortAlgorithms'; +import { algorithmContent, algorithmMap, summarizeAlgorithmRun } from './utils/sortAlgorithms'; const ARRAY_MIN = 8; const ARRAY_MAX = 64; @@ -9,13 +9,7 @@ const SPEED_MAX = 300; const DEFAULT_SIZE = 28; const DEFAULT_SPEED = 110; -const algorithmOptions = [ - { value: 'bubble', label: 'Bubble Sort' }, - { value: 'selection', label: 'Selection Sort' }, - { value: 'insertion', label: 'Insertion Sort' }, - { value: 'quick', label: 'Quick Sort' }, - { value: 'heap', label: 'Heap Sort' } -]; +const algorithmKeys = Object.keys(algorithmMap); const presetOptions = [ { @@ -50,14 +44,12 @@ const createIdleState = (array) => ({ }); const getAlgorithmLabel = (value) => - algorithmOptions.find((option) => option.value === value)?.label ?? value; + algorithmContent[value]?.title ?? value; -const clampArraySize = (size) => Math.min(Math.max(size, ARRAY_MIN), ARRAY_MAX); const getTallestArrayValue = (array) => Math.max(...array.map((value) => Math.abs(value)), 1); const createComparisonPlaceholder = (key) => ({ key, - label: getAlgorithmLabel(key), comparisons: '-', moves: '-', steps: '-', @@ -65,12 +57,94 @@ const createComparisonPlaceholder = (key) => ({ resultArray: [] }); +const statMetrics = [ + ['comparisons', 'Vergleiche'], + ['moves', 'Bewegungen'], + ['steps', 'Schritte'] +]; + +const theoryMetrics = [ + ['bestCase', 'Best Case'], + ['averageCase', 'Average Case'], + ['worstCase', 'Worst Case'], + ['stability', 'Stabilität'] +]; + +const AlgorithmSelect = ({ id, label, value, onChange }) => ( + +); + +const RangeField = ({ id, label, value, displayValue = value, min, max, onChange }) => ( + +); + +const PresetButtons = ({ size, onApply, includeRandom = false }) => ( + <> + {includeRandom && ( + + )} + {presetOptions.map((preset) => ( + + ))} + +); + +const MetricCards = ({ metrics, values, className }) => ( + <> + {metrics.map(([key, label]) => ( +
+ {label} + {values[key]} +
+ ))} + +); + +const CompactBars = ({ values, idPrefix }) => { + const tallestValue = getTallestArrayValue(values); + + return ( +
+ {values.map((value, index) => ( + + ))} +
+ ); +}; + function App() { const initialArray = useMemo(() => createRandomArray(DEFAULT_SIZE), []); const [activeTab, setActiveTab] = useState('visualizer'); const [algorithm, setAlgorithm] = useState('bubble'); - const [arraySize, setArraySize] = useState(DEFAULT_SIZE); const [speed, setSpeed] = useState(DEFAULT_SPEED); const [baseArray, setBaseArray] = useState(initialArray); const [arrayInput, setArrayInput] = useState(() => formatArrayInput(initialArray)); @@ -79,19 +153,15 @@ function App() { const [steps, setSteps] = useState([]); const [currentStep, setCurrentStep] = useState(0); const [isPlaying, setIsPlaying] = useState(false); - const [isPaused, setIsPaused] = useState(false); const [generationTimeMs, setGenerationTimeMs] = useState(0); const [isDirty, setIsDirty] = useState(true); const [compareLeftAlgorithm, setCompareLeftAlgorithm] = useState('bubble'); const [compareRightAlgorithm, setCompareRightAlgorithm] = useState('quick'); - const [compareArraySize, setCompareArraySize] = useState(DEFAULT_SIZE); const [compareArray, setCompareArray] = useState(() => createRandomArray(DEFAULT_SIZE)); const [comparisonRows, setComparisonRows] = useState([]); const timeoutRef = useRef(null); - const audioContextRef = useRef(null); - const previousSortedRef = useRef([]); useEffect(() => { if (!isPlaying || steps.length === 0) { @@ -100,7 +170,6 @@ function App() { if (currentStep >= steps.length - 1) { setIsPlaying(false); - setIsPaused(false); return undefined; } @@ -121,46 +190,14 @@ function App() { } }, [currentStep, steps]); - useEffect(() => { - const currentSorted = visualState.sorted; - const previousSorted = previousSortedRef.current; - - if (!isPlaying) { - previousSortedRef.current = currentSorted; - return; - } - - const newlySorted = currentSorted.filter((index) => !previousSorted.includes(index)); - if (newlySorted.length > 0 && audioContextRef.current) { - const audioContext = audioContextRef.current; - const now = audioContext.currentTime; - - newlySorted.forEach((_, order) => { - const oscillator = audioContext.createOscillator(); - const gainNode = audioContext.createGain(); - const startAt = now + (order * 0.035); - - oscillator.type = 'sine'; - oscillator.frequency.setValueAtTime(620 + (order * 45), startAt); - gainNode.gain.setValueAtTime(0.0001, startAt); - gainNode.gain.exponentialRampToValueAtTime(0.08, startAt + 0.01); - gainNode.gain.exponentialRampToValueAtTime(0.0001, startAt + 0.14); - - oscillator.connect(gainNode); - gainNode.connect(audioContext.destination); - oscillator.start(startAt); - oscillator.stop(startAt + 0.15); - }); - } - - previousSortedRef.current = currentSorted; - }, [isPlaying, visualState.sorted]); - const explanation = useMemo(() => algorithmContent[algorithm], [algorithm]); + const arraySize = baseArray.length; + const compareArraySize = compareArray.length; const tallestValue = useMemo( () => getTallestArrayValue(visualState.array), [visualState.array] ); + const isPaused = !isPlaying && currentStep > 0 && currentStep < steps.length - 1; const progress = steps.length > 1 ? Math.round((currentStep / (steps.length - 1)) * 100) : 0; const statusLabel = isPlaying ? 'Läuft' @@ -185,14 +222,11 @@ function App() { setCurrentStep(0); setGenerationTimeMs(0); setIsPlaying(false); - setIsPaused(false); setIsDirty(true); - previousSortedRef.current = []; }; const applyArray = (nextArray) => { setBaseArray(nextArray); - setArraySize(clampArraySize(nextArray.length)); setArrayInput(formatArrayInput(nextArray)); setArrayInputError(''); setVisualState(createIdleState(nextArray)); @@ -201,29 +235,9 @@ function App() { const applyCompareArray = (nextArray) => { setCompareArray(nextArray); - setCompareArraySize(clampArraySize(nextArray.length)); setComparisonRows([]); }; - const prepareAudio = () => { - if (typeof window === 'undefined') { - return; - } - - const AudioContextClass = window.AudioContext || window.webkitAudioContext; - if (!AudioContextClass) { - return; - } - - if (!audioContextRef.current) { - audioContextRef.current = new AudioContextClass(); - } - - if (audioContextRef.current.state === 'suspended') { - audioContextRef.current.resume().catch(() => {}); - } - }; - const generateSteps = () => { const selectedAlgorithm = algorithmMap[algorithm]; const start = performance.now(); @@ -235,7 +249,6 @@ function App() { setCurrentStep(0); setVisualState(generatedSteps[0]); setIsDirty(false); - previousSortedRef.current = generatedSteps[0]?.sorted ?? []; return generatedSteps; }; @@ -252,7 +265,6 @@ function App() { }; const handleStart = () => { - prepareAudio(); const preparedSteps = !steps.length || isDirty ? generateSteps() : steps; if (preparedSteps.length <= 1) { return; @@ -261,47 +273,22 @@ function App() { if (currentStep >= preparedSteps.length - 1) { setCurrentStep(0); setVisualState(preparedSteps[0]); - previousSortedRef.current = preparedSteps[0]?.sorted ?? []; } setIsPlaying(true); - setIsPaused(false); }; const handleReset = () => { - if (timeoutRef.current) { - window.clearTimeout(timeoutRef.current); - } - - setIsPlaying(false); - setIsPaused(false); - setCurrentStep(0); - setGenerationTimeMs(0); - setSteps([]); + resetPlaybackState(); setVisualState(createIdleState(baseArray)); - setIsDirty(true); - previousSortedRef.current = []; }; const handleCompare = () => { - const rows = [compareLeftAlgorithm, compareRightAlgorithm].map((algorithmKey) => { - const start = performance.now(); - const generatedSteps = algorithmMap[algorithmKey](compareArray); - const end = performance.now(); - const finalState = generatedSteps[generatedSteps.length - 1]; - - return { - key: algorithmKey, - label: getAlgorithmLabel(algorithmKey), - comparisons: finalState.stats.comparisons, - moves: finalState.stats.moves, - steps: finalState.stats.steps, - generationTimeMs: Number((end - start).toFixed(2)), - resultArray: finalState.array - }; - }); - - setComparisonRows(rows); + setComparisonRows( + [compareLeftAlgorithm, compareRightAlgorithm].map((algorithmKey) => ({ + ...summarizeAlgorithmRun(algorithmKey, compareArray) + })) + ); }; return ( @@ -351,48 +338,35 @@ function App() {

Array, Geschwindigkeit und Algorithmus für die Simulation anpassen.

- - - - - + { + setAlgorithm(nextAlgorithm); + setVisualState(createIdleState(baseArray)); + resetPlaybackState(); + }} + /> + + applyArray(createRandomArray(size))} + /> + +
@@ -409,24 +383,20 @@ function App() {

{arrayInputError || 'Ganze Zahlen mit Komma, Leerschlag oder Semikolon trennen.'}

-
- {presetOptions.map((preset) => ( - - ))} +
-
- - - + { + setCompareLeftAlgorithm(value); + setComparisonRows([]); + }} + /> + { + setCompareRightAlgorithm(value); + setComparisonRows([]); + }} + /> + applyCompareArray(createRandomArray(size))} + />
- - {presetOptions.map((preset) => ( - - ))} +
@@ -613,40 +531,15 @@ function App() {
{comparisonDisplayRows.map((row, rowIndex) => (
-

{row.label}

- {row.resultArray.length > 0 && (() => { - const tallestResultValue = getTallestArrayValue(row.resultArray); - - return ( -
- {row.resultArray.map((value, index) => { - const normalizedHeight = Math.max((Math.abs(value) / tallestResultValue) * 100, 8); - - return ( - - ); - })} -
- ); - })()} +

{getAlgorithmLabel(row.key)}

+ {row.resultArray.length > 0 && }
-
-
Vergleiche
-
{row.comparisons}
-
-
-
Bewegungen
-
{row.moves}
-
-
-
Schritte
-
{row.steps}
-
+ {statMetrics.map(([key, label]) => ( +
+
{label}
+
{row[key]}
+
+ ))}
Berechnungs- und Schrittgenerierungszeit
{row.generationTimeMs} ms
diff --git a/src/main.jsx b/src/main.jsx index d0f5e13..e703a55 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,69 +1,5 @@ -import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App.jsx'; import './styles.css'; -class ErrorBoundary extends React.Component { - constructor(props) { - super(props); - this.state = { error: null }; - } - - static getDerivedStateFromError(error) { - return { error }; - } - - render() { - if (this.state.error) { - return ( -
-
-

- SortLab Fehleranzeige -

-

Die React-App ist abgestürzt.

-

- Das ist absichtlich sichtbar, damit wir keinen leeren Bildschirm mehr haben. -

-
-              {String(this.state.error)}
-            
-
-
- ); - } - - return this.props.children; - } -} - -ReactDOM.createRoot(document.getElementById('root')).render( - - - -); +ReactDOM.createRoot(document.getElementById('root')).render(); diff --git a/src/styles.css b/src/styles.css index a144bff..b4c7efe 100644 --- a/src/styles.css +++ b/src/styles.css @@ -278,16 +278,14 @@ button:disabled { opacity: 0.5; } -button.primary, -button.primary.accent { +button.primary { background: var(--accent); border-color: var(--accent-dark); color: #ffffff; font-weight: 700; } -button.primary:hover, -button.primary.accent:hover { +button.primary:hover { background: var(--accent-dark); } @@ -300,10 +298,6 @@ button.secondary:hover { border-color: var(--compare); } -.full-width { - width: 100%; -} - .field-hint, .field-error { margin: 0; @@ -330,13 +324,6 @@ button.secondary:hover { grid-column: 1 / -1; } -.split-head { - display: flex; - justify-content: space-between; - gap: 12px; - align-items: flex-start; -} - .legend { display: flex; flex-wrap: wrap; @@ -455,22 +442,10 @@ button.secondary:hover { transition: border-color 0.16s ease, background-color 0.16s ease; } -.stat-card:nth-child(1) { +.stat-card { border-left: 4px solid var(--accent); } -.stat-card:nth-child(2) { - border-left: 4px solid var(--move); -} - -.stat-card:nth-child(3) { - border-left: 4px solid var(--compare); -} - -.stat-card:nth-child(4) { - border-left: 4px solid var(--sorted); -} - .info-card { border-left: 4px solid var(--warning); } @@ -630,10 +605,6 @@ button.secondary:hover { grid-column: auto; } - .split-head { - display: grid; - } - .chart { gap: 4px; } diff --git a/src/utils/arrayInput.js b/src/utils/arrayInput.js index 285de56..446b34d 100644 --- a/src/utils/arrayInput.js +++ b/src/utils/arrayInput.js @@ -1,4 +1,3 @@ -export const ARRAY_INPUT_MIN_LENGTH = 1; export const ARRAY_INPUT_MAX_LENGTH = 64; export const ARRAY_INPUT_MIN_VALUE = -99; export const ARRAY_INPUT_MAX_VALUE = 99; @@ -17,13 +16,6 @@ export const parseArrayInput = (input) => { const parts = trimmed.split(/[,\s;]+/).filter(Boolean); - if (parts.length < ARRAY_INPUT_MIN_LENGTH) { - return { - values: [], - error: 'Gib mindestens eine Zahl ein.' - }; - } - if (parts.length > ARRAY_INPUT_MAX_LENGTH) { return { values: [], diff --git a/src/utils/sortAlgorithms.js b/src/utils/sortAlgorithms.js index c34ecc5..05d8c53 100644 --- a/src/utils/sortAlgorithms.js +++ b/src/utils/sortAlgorithms.js @@ -13,17 +13,24 @@ const cloneState = ({ array, compared = [], swapped = [], sorted = [], stats }) }); const sortedFromSet = (set) => Array.from(set).sort((left, right) => left - right); +const sortedHead = (length) => Array.from({ length }, (_, index) => index); +const sortedTail = (array, length) => + Array.from({ length }, (_, offset) => array.length - 1 - offset); const markAllSorted = (array, stats) => cloneState({ array, - sorted: array.map((_, index) => index), + sorted: sortedHead(array.length), stats }); const pushFinalStateIfNeeded = (steps, array, stats) => { + if (steps.length === 0) { + return [markAllSorted(array, stats)]; + } + const lastStep = steps[steps.length - 1]; - const allSorted = array.map((_, index) => index); + const allSorted = sortedHead(array.length); const alreadyComplete = lastStep.sorted.length === allSorted.length && lastStep.sorted.every((value, index) => value === allSorted[index]); @@ -35,80 +42,92 @@ const pushFinalStateIfNeeded = (steps, array, stats) => { return steps; }; -const bubbleSort = (source) => { +const createRecorder = (array, stats, { recordSteps = true, getSorted = () => [] } = {}) => { + const steps = recordSteps ? [cloneState({ array, stats })] : []; + + const push = (overrides = {}) => { + if (!recordSteps) { + return; + } + + steps.push( + cloneState({ + array, + sorted: getSorted(), + stats, + ...overrides + }) + ); + }; + + return { + steps, + push, + finish: () => pushFinalStateIfNeeded(steps, array, stats) + }; +}; + +const countComparison = (stats) => { + stats.comparisons += 1; + stats.steps += 1; +}; + +const countMove = (stats) => { + stats.moves += 1; + stats.steps += 1; +}; + +const bubbleSort = (source, options = {}) => { const array = [...source]; const stats = { ...baseStats }; - const steps = [cloneState({ array, stats })]; + const recorder = createRecorder(array, stats, options); for (let end = array.length - 1; end > 0; end -= 1) { let swappedInPass = false; for (let index = 0; index < end; index += 1) { - stats.comparisons += 1; - stats.steps += 1; - steps.push( - cloneState({ - array, - compared: [index, index + 1], - sorted: Array.from({ length: array.length - 1 - end }, (_, offset) => array.length - 1 - offset), - stats - }) - ); + countComparison(stats); + recorder.push({ + compared: [index, index + 1], + sorted: sortedTail(array, array.length - 1 - end) + }); if (array[index] > array[index + 1]) { [array[index], array[index + 1]] = [array[index + 1], array[index]]; - stats.moves += 1; - stats.steps += 1; + countMove(stats); swappedInPass = true; - - steps.push( - cloneState({ - array, - compared: [index, index + 1], - swapped: [index, index + 1], - sorted: Array.from({ length: array.length - 1 - end }, (_, offset) => array.length - 1 - offset), - stats - }) - ); + recorder.push({ + compared: [index, index + 1], + swapped: [index, index + 1], + sorted: sortedTail(array, array.length - 1 - end) + }); } } - steps.push( - cloneState({ - array, - sorted: Array.from({ length: array.length - end }, (_, offset) => array.length - 1 - offset), - stats - }) - ); + recorder.push({ sorted: sortedTail(array, array.length - end) }); if (!swappedInPass) { break; } } - return pushFinalStateIfNeeded(steps, array, stats); + return recorder.finish(); }; -const selectionSort = (source) => { +const selectionSort = (source, options = {}) => { const array = [...source]; const stats = { ...baseStats }; - const steps = [cloneState({ array, stats })]; + const recorder = createRecorder(array, stats, options); for (let start = 0; start < array.length; start += 1) { let minIndex = start; for (let index = start + 1; index < array.length; index += 1) { - stats.comparisons += 1; - stats.steps += 1; - - steps.push( - cloneState({ - array, - compared: [minIndex, index], - sorted: Array.from({ length: start }, (_, offset) => offset), - stats - }) - ); + countComparison(stats); + recorder.push({ + compared: [minIndex, index], + sorted: sortedHead(start) + }); if (array[index] < array[minIndex]) { minIndex = index; @@ -117,78 +136,50 @@ const selectionSort = (source) => { if (minIndex !== start) { [array[start], array[minIndex]] = [array[minIndex], array[start]]; - stats.moves += 1; - stats.steps += 1; - - steps.push( - cloneState({ - array, - swapped: [start, minIndex], - sorted: Array.from({ length: start }, (_, offset) => offset), - stats - }) - ); + countMove(stats); + recorder.push({ + swapped: [start, minIndex], + sorted: sortedHead(start) + }); } - steps.push( - cloneState({ - array, - sorted: Array.from({ length: start + 1 }, (_, offset) => offset), - stats - }) - ); + recorder.push({ sorted: sortedHead(start + 1) }); } - return pushFinalStateIfNeeded(steps, array, stats); + return recorder.finish(); }; -const insertionSort = (source) => { +const insertionSort = (source, options = {}) => { const array = [...source]; const stats = { ...baseStats }; - const steps = [cloneState({ array, stats })]; + const recorder = createRecorder(array, stats, options); for (let index = 1; index < array.length; index += 1) { const value = array[index]; let position = index - 1; - steps.push( - cloneState({ - array, - compared: [index], - sorted: Array.from({ length: index }, (_, offset) => offset), - stats - }) - ); + recorder.push({ + compared: [index], + sorted: sortedHead(index) + }); while (position >= 0) { - stats.comparisons += 1; - stats.steps += 1; - - steps.push( - cloneState({ - array, - compared: [position, position + 1], - sorted: Array.from({ length: index }, (_, offset) => offset), - stats - }) - ); + countComparison(stats); + recorder.push({ + compared: [position, position + 1], + sorted: sortedHead(index) + }); if (array[position] <= value) { break; } array[position + 1] = array[position]; - stats.moves += 1; - stats.steps += 1; - - steps.push( - cloneState({ - array, - swapped: [position, position + 1], - sorted: Array.from({ length: index }, (_, offset) => offset), - stats - }) - ); + countMove(stats); + recorder.push({ + swapped: [position, position + 1], + sorted: sortedHead(index) + }); position -= 1; } @@ -198,55 +189,40 @@ const insertionSort = (source) => { if (movedToNewPosition) { array[targetPosition] = value; - stats.moves += 1; - stats.steps += 1; + countMove(stats); } - steps.push( - cloneState({ - array, - swapped: movedToNewPosition ? [targetPosition] : [], - sorted: Array.from({ length: index + 1 }, (_, offset) => offset), - stats - }) - ); + recorder.push({ + swapped: movedToNewPosition ? [targetPosition] : [], + sorted: sortedHead(index + 1) + }); } - return pushFinalStateIfNeeded(steps, array, stats); + return recorder.finish(); }; -const quickSort = (source) => { +const quickSort = (source, options = {}) => { const array = [...source]; const stats = { ...baseStats }; - const steps = [cloneState({ array, stats })]; const finalized = new Set(); - - const pushState = (overrides = {}) => { - steps.push( - cloneState({ - array, - sorted: sortedFromSet(finalized), - stats, - ...overrides - }) - ); - }; + const recorder = createRecorder(array, stats, { + ...options, + getSorted: () => sortedFromSet(finalized) + }); const partition = (low, high) => { const pivotValue = array[high]; let storeIndex = low; for (let index = low; index < high; index += 1) { - stats.comparisons += 1; - stats.steps += 1; - pushState({ compared: [index, high] }); + countComparison(stats); + recorder.push({ compared: [index, high] }); if (array[index] < pivotValue) { if (index !== storeIndex) { [array[index], array[storeIndex]] = [array[storeIndex], array[index]]; - stats.moves += 1; - stats.steps += 1; - pushState({ compared: [index, high], swapped: [index, storeIndex] }); + countMove(stats); + recorder.push({ compared: [index, high], swapped: [index, storeIndex] }); } storeIndex += 1; @@ -255,13 +231,12 @@ const quickSort = (source) => { if (storeIndex !== high) { [array[storeIndex], array[high]] = [array[high], array[storeIndex]]; - stats.moves += 1; - stats.steps += 1; - pushState({ swapped: [storeIndex, high] }); + countMove(stats); + recorder.push({ swapped: [storeIndex, high] }); } finalized.add(storeIndex); - pushState(); + recorder.push(); return storeIndex; }; @@ -272,7 +247,7 @@ const quickSort = (source) => { if (low === high) { finalized.add(low); - pushState(); + recorder.push(); return; } @@ -282,25 +257,17 @@ const quickSort = (source) => { }; sort(0, array.length - 1); - return pushFinalStateIfNeeded(steps, array, stats); + return recorder.finish(); }; -const heapSort = (source) => { +const heapSort = (source, options = {}) => { const array = [...source]; const stats = { ...baseStats }; - const steps = [cloneState({ array, stats })]; const finalized = new Set(); - - const pushState = (overrides = {}) => { - steps.push( - cloneState({ - array, - sorted: sortedFromSet(finalized), - stats, - ...overrides - }) - ); - }; + const recorder = createRecorder(array, stats, { + ...options, + getSorted: () => sortedFromSet(finalized) + }); const heapify = (heapSize, rootIndex) => { let currentRoot = rootIndex; @@ -311,9 +278,8 @@ const heapSort = (source) => { let largest = currentRoot; if (leftChild < heapSize) { - stats.comparisons += 1; - stats.steps += 1; - pushState({ compared: [largest, leftChild] }); + countComparison(stats); + recorder.push({ compared: [largest, leftChild] }); if (array[leftChild] > array[largest]) { largest = leftChild; @@ -321,9 +287,8 @@ const heapSort = (source) => { } if (rightChild < heapSize) { - stats.comparisons += 1; - stats.steps += 1; - pushState({ compared: [largest, rightChild] }); + countComparison(stats); + recorder.push({ compared: [largest, rightChild] }); if (array[rightChild] > array[largest]) { largest = rightChild; @@ -335,9 +300,8 @@ const heapSort = (source) => { } [array[currentRoot], array[largest]] = [array[largest], array[currentRoot]]; - stats.moves += 1; - stats.steps += 1; - pushState({ swapped: [currentRoot, largest] }); + countMove(stats); + recorder.push({ swapped: [currentRoot, largest] }); currentRoot = largest; } }; @@ -348,10 +312,9 @@ const heapSort = (source) => { for (let end = array.length - 1; end > 0; end -= 1) { [array[0], array[end]] = [array[end], array[0]]; - stats.moves += 1; - stats.steps += 1; + countMove(stats); finalized.add(end); - pushState({ swapped: [0, end] }); + recorder.push({ swapped: [0, end] }); heapify(end, 0); } @@ -359,7 +322,7 @@ const heapSort = (source) => { finalized.add(0); } - return pushFinalStateIfNeeded(steps, array, stats); + return recorder.finish(); }; export const algorithmMap = { @@ -370,6 +333,21 @@ export const algorithmMap = { heap: heapSort }; +export const summarizeAlgorithmRun = (algorithmKey, source) => { + const start = performance.now(); + const [finalState] = algorithmMap[algorithmKey](source, { recordSteps: false }); + const end = performance.now(); + + return { + key: algorithmKey, + comparisons: finalState.stats.comparisons, + moves: finalState.stats.moves, + steps: finalState.stats.steps, + generationTimeMs: Number((end - start).toFixed(2)), + resultArray: finalState.array + }; +}; + export const algorithmContent = { bubble: { title: 'Bubble Sort',