Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 60 additions & 86 deletions algorithms-helper/selection-sort.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,76 @@
import { ActiveBar, AnimateFunctionParams } from "../types"
import { changeBarsColor, getAllBars, makeBarsActive, postSortAnimation } from "../utils"

const selectionSort = (inputArray: number[]) => {
let animationSequence: [number, number][] = []
let unsortedArray = [...inputArray]
let arrayState: number[][] = []

for (let x = 0; x < unsortedArray.length; x++) {
let currentMinHasChanged = false
let currentMin = unsortedArray[x]
let currentMinIdx = x

// These indexes will be stored as an item in the animation sequence array
let selectedBars = [currentMinIdx, currentMinIdx] as [number, number]
import { AnimateFunctionParams } from "../types"
import {
changeBarsColor,
postSortAnimation,
makeBarsActive,
getNumberFromHeightString,
} from "../utils"

interface SelectionSortAnimationItem {
compare?: [number, number]
swap?: [number, number]
}

for (let y = x + 1; y < unsortedArray.length; y++) {
let currentItem = unsortedArray[y]
export const selectionSort = (unsortedArray: number[]) => {
const array = unsortedArray.slice()
const arrayLength = array.length
const animations: SelectionSortAnimationItem[] = []

if (currentItem < currentMin) {
currentMin = currentItem
currentMinIdx = y
currentMinHasChanged = true
for (let i = 0; i < arrayLength; i++) {
let minIdx = i
for (let j = i + 1; j < arrayLength; j++) {
if (array[minIdx] > array[j]) {
minIdx = j
}
animations.push({ compare: [i, j] })
}

if (currentMinHasChanged) {
// Swap current value with the new currentMin value
let temp = unsortedArray[currentMinIdx]
unsortedArray[currentMinIdx] = unsortedArray[x]
unsortedArray[x] = temp

// currentMin value has changed, so we need to reassign selectedBars[1]
selectedBars[1] = currentMinIdx
}

animationSequence.push(selectedBars)

// Spread operator is needed to create a shallow copy of the current unsortedArray
arrayState.push([...unsortedArray])
animations.push({ swap: [i, minIdx] })
;[array[i], array[minIdx]] = [array[minIdx], array[i]]
}

return [arrayState, animationSequence]
return animations
}

export const animateSelectionSort = (params: AnimateFunctionParams) => {
const { barHeights, palette, sortingSpeed, callback } = params
const [arrayStates, animationSequence] = selectionSort(barHeights)
const bars = getAllBars()

for (let i = 0; i < animationSequence.length; i++) {
let firstIteration = i === 0
let lastIteration = i === animationSequence.length - 1

const [bar1Idx, bar2Idx] = animationSequence[i]

const activeBar1 = bars[bar1Idx]
const activeBar2 = bars[bar2Idx]

const activeBar1Height = arrayStates[i][bar2Idx]
const activeBar2Height = arrayStates[i][bar1Idx]

let barsToActiveBars: ActiveBar[] = [
{
element: activeBar1,
height: activeBar2Height,
},
{
element: activeBar2,
height: activeBar1Height,
},
]

const { barHeights, bars, palette, sortingSpeed, callback } = params
const animations = selectionSort(barHeights)
let previousActiveIdxs = []
animations.forEach((item, idx) => {
setTimeout(() => {
if (!firstIteration) {
// Reset previously active bars' color
let prevAnimationSequence = animationSequence[i - 1]
let [prevBar1Idx, prevBar2Idx] = prevAnimationSequence
let previousActiveBars = [bars[prevBar1Idx], bars[prevBar2Idx]]
changeBarsColor(previousActiveBars, palette.inactive)
if (idx > 0) {
if (!animations[idx - 1].swap) {
const previousItem = animations[idx - 1].compare
changeBarsColor(
[bars[previousItem[0]], bars[previousItem[1]]],
palette.inactive
)
}
}

makeBarsActive(barsToActiveBars, palette.active)

if (lastIteration) {
// Revert the last bar to an inactive bar
setTimeout(() => {
let lastBar = activeBar1
changeBarsColor(lastBar, palette.inactive)
if (item.compare) {
const [idx1, idx2] = item.compare
previousActiveIdxs.push([idx1, idx2])
changeBarsColor([bars[idx1], bars[idx2]], palette.active)
} else if (item.swap) {
const [idx1, idx2] = item.swap
const heights = [
getNumberFromHeightString(bars[idx1].style.height),
getNumberFromHeightString(bars[idx2].style.height),
]
makeBarsActive(
[
{ element: bars[idx1], height: heights[1] },
{ element: bars[idx2], height: heights[0] },
],
palette.swapping
)
}

// Because this animate function executes asynchronous function (setTimeout)
// if we wanted to do something right after this function is done running,
// we have to put the code inside the last setTimeout.
// Otherwise, the code will get executed without waiting for the setTimeouts
// to get executed.
postSortAnimation(bars, palette.active)
if (callback) callback()
}, sortingSpeed)
if (callback && idx === animations.length - 1) {
postSortAnimation(bars as any, palette.active)
callback()
}
}, i * sortingSpeed)
}
}, idx * sortingSpeed)
})
}
6 changes: 3 additions & 3 deletions constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SortingAlgorithms, SortingSpeeds } from "./types"

export const ALGORITHMS_LIST: SortingAlgorithms[] = ["Selection", "Insertion", "Bubble"]
export const sortingSpeedTable: SortingSpeeds = {
slow: 160,
normal: 80,
fast: 40,
slow: 200,
normal: 70,
fast: 3,
}
18 changes: 11 additions & 7 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @ts-ignore
import styles from "../styles/Home.module.scss"

import React, { useEffect, useState } from "react"
import React, { useEffect, useRef, useState } from "react"
import {
changeBarsColor,
generateBarHeights,
Expand Down Expand Up @@ -43,6 +43,8 @@ const Home: React.FC = () => {
const palette = {
active: geistUIPalette.foreground,
inactive: geistUIPalette.errorLight,
marked: geistUIPalette.warningDark,
swapping: geistUIPalette.success,
}

const [barHeights, setBarHeights] = useState<number[]>([])
Expand All @@ -52,27 +54,29 @@ const Home: React.FC = () => {
"Selection"
)
const [sortingSpeed, setSortingSpeed] = useState<keyof SortingSpeeds>("normal")
const barsRef = useRef<HTMLElement[]>(null)

useEffect(resetBars, [arrayLength])
useEffect(resetBars, [selectedAlgorithm])
useEffect(resetBars, [arrayLength, selectedAlgorithm])

function resetBars() {
const newBarHeights = generateBarHeights(arrayLength)
setBarHeights(newBarHeights)
setSortingState("Sort")
requestAnimationFrame(() => {
const barsDomEl = getAllBars()
const sampleBg = barsDomEl[5].style.backgroundColor
barsRef.current = Array.from(barsDomEl)
const sampleBg = barsRef.current[5].style.backgroundColor
if (sampleBg === hexToRgb(palette.active)) {
changeBarsColor(barsDomEl, palette.inactive)
changeBarsColor(barsRef.current, palette.inactive)
}
})
}

function triggerAnimation() {
function triggerAnimation(bars: HTMLElement[]) {
setSortingState("Sorting")
startAnimation({
barHeights,
bars,
palette,
sortingAlgorithm: selectedAlgorithm,
sortingSpeed: sortingSpeedTable[sortingSpeed],
Expand Down Expand Up @@ -106,7 +110,7 @@ const Home: React.FC = () => {
sortingSpeed={sortingSpeed}
/>
<Spacer y={1.5} />
<SortButton onClick={triggerAnimation} />
<SortButton onClick={() => triggerAnimation(barsRef.current)} />
<Spacer y={0.6} />
<ResetButton onClick={resetBars} />
<Spacer y={1.7} />
Expand Down
4 changes: 4 additions & 0 deletions styles/Home.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@
justify-content: space-evenly;
width: calc(100vw - 330px);
}

.bar {
transition: height 0.3s;
}
3 changes: 2 additions & 1 deletion types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export interface SortingSpeeds {
}
export interface AnimateFunctionParams {
barHeights: number[]
palette: { active: string; inactive: string }
bars: HTMLElement[]
palette: { active: string; inactive: string; swapping: string }
sortingSpeed: number
callback?: () => void
}
6 changes: 4 additions & 2 deletions utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,18 @@ export const getNumberFromHeightString = (height: string) => {

interface StartAnimationParams {
barHeights: number[]
palette: { active: string; inactive: string }
bars
palette: { active: string; inactive: string; swapping: string }
sortingAlgorithm: SortingAlgorithms
sortingSpeed: number
callback?: () => void
}

export const startAnimation = (params: StartAnimationParams) => {
const { barHeights, palette, sortingAlgorithm, sortingSpeed, callback } = params
const { barHeights, bars, palette, sortingAlgorithm, sortingSpeed, callback } = params
const animationParams = {
barHeights,
bars,
palette,
sortingSpeed,
callback,
Expand Down