Imagine you are in an interview and you get asked "can you implement bubble sort"...and you answer the interviewer "sure, do you want that in JavaScript or CSS".
What a flex would that be?
Now, I know what you are thinking. "no interviewer will be impressed by you creating an animation that simulates bubble sort". And you would be right!
But what if we could create a functioning bubble sort algorithm...in pure CSS...and add visualisations to it?
Well, you have come to the right place!
The Demo
Instructions: There are 5 values at the top of the :root
element:
:root{
--val1: 12;
--val2: 16;
--val3: 9;
--val4: 14;
--val5: 7;
These are our unsorted array!
So the above represents: [12,16,9,14,7]
.
And you can change those values (any value between 0 and 20) and then press "run" in the Codepen UI and it will actually sort them for you!
warning: on mobile the last few animations might not play and just go blank. On PC your fan may spin up!
This is a limitation of using so many calculations that rely on previous calculations...I am not sure if it runs out of memory or what, but I defo pushed the limits of CSS here!
Anyway, with the warnings out of the way, give it a go (you may need to press "rerun" in the bottom right as the animation only plays once)!
Explanation
Look, this is silly, so I am not going to do a tutorial, but there are a couple of things that are interesting:
Getting a boolean for v2 > v1
--is-1-greater-2-step-1: Min(1, Max(var(--arr1s0) - var(--arr2s0), 0));
Looks complicated, but it isn't, we are performing the following operations:
- subtract the value of position 2 from the value of position 1 in our array. (lets call this "diff1and2" for ease)
- find the maximum of "diff1and2" and 0. We do this as a way of saying "if 1 is bigger than 2, we want a positive value, but if 2 is bigger than 1 we want to return 0". Lets call the result of this "1greaterOrZero".
- then we take "OneGreaterOrZero " and make sure it is not greater than 1 by using
Min
. So if "OneGreaterOrZero" was 6, we would reduce that to 1, but if it was 0 then we would return 0.
Still confused? Here it is in JS effectively:
let pos1 = 7;
let pos2 = 15;
let diff1and2 = pos1 - pos2;
//if "diff1and2" is negative the next step will change it to 0;
let OneGreaterOrZero = Math.max(diff1and2, 0);
let result = Math.min(1, OneGreaterOrZero);
console.log(diff1and2, OneGreaterOrZero, result); //always between 0 and 1 as false / true representation.
swapping array positions
So how do we swap positions in our "array"?
For bubble sort to work we need to be able to swap 2 values if the first value is larger than the second.
We can't do any temp variable magic like we would in JS.
Well, that is what this is for:
--arr1s1: calc(var(--is-2-greater-1-step-1) * var(--arr1s0) + var(--is-1-greater-2-step-1) * var(--arr2s0));
--arr2s1: calc(var(--is-1-greater-2-step-1) * var(--arr1s0) + var(--is-2-greater-1-step-1) * var(--arr2s0));
Yet again, looks complicated, is actually reasonably simple in principle.
In our previous "function", we created a boolean to see if 1 was greater than 2. So we have either a 1 or a 0 there.
We can also easily get the inverse of this:
--is-2-greater-1-step-1: (1 - var(--is-1-greater-2-step-1));
The beauty of this is that we can now do the following trick:
// in bubble sort, if 1
origArray = [7,2];
// we run our previous functions to get our 2 variables:
oneIsGreater = 1;
twoIsGreater = 0;
// we can now multiply the values together. If [2] is greater than one then we will return the same value. But if [1] is greater than [2] then we will swap the values.
newArray[0] = (twoIsGreater * origArray[0]) + (oneIsGreater * origArray[1]);
newArray[1] = (oneIsGreater * origArray[0]) + (twoIsGreater * origArray[1]);
// which is the same as this:
newArray[0] = 0 * 7 + 1 * 2; //2
newArray[1] = 1 * 7 + 0 * 2; //7
Neat trick huh? and if oneIsGreater
and twoIsGreater
were swapped then it would just return the original values!
origArray = [7,2];
oneIsGreater = 0;
twoIsGreater = 1;
//same "function"
newArray[0] = (twoIsGreater * origArray[0]) + (oneIsGreater * origArray[1]);
newArray[1] = (oneIsGreater * origArray[0]) + (twoIsGreater * origArray[1]);
// which is the same as this:
newArray[0] = 1 * 7 + 0 * 2; //7
newArray[1] = 0 * 7 + 1 * 2; //2
And that is effectively all we need to do a bubble sort!
The only reason we have so much CSS is because we cannot do loops in vanilla CSS (yet). So we have to manually write out the swaps for each stage of bubble sort:
- check and if needed swap 1 and 2
- check and if needed swap 2 and 3
- check and if needed swap 3 and 4
- check and if needed swap 4 and 5
- check and if needed swap 1 and 2
- check and if needed swap 2 and 3
- check and if needed swap 3 and 4
- check and if needed swap 1 and 2
- check and if needed swap 2 and 3
- check and if needed swap 1 and 2
That's all folks!
As I said, not a tutorial. But I did want to introduce some interesting CSS "switches" and "booleans" that may become useful in some strange scenario in the future for you!
I hope you enjoy my silliness.
Now I just have to wait for someone to ask me if I can implement Bubble sort so I can flex on them with my CSS silliness! 🤣💗