100+ Ways to Solve a Specific Programming Problem in 50+ Languages
José Valim and I asked developers how they would solve a specific problem in their language of choice. How would you do it?
If you prefer watching video, I recorded a video version of this blog post.
While developing an Elixir application I ran into a slight road block on how to solve a specific problem. It involved a nested loop, updating 2 counters and also updating a Map (a dictionary-like data structure).
This post outlines the specific problem, how I solved it in Python, why I struggled solving it in Elixir (spoiler alert: it has nothing to do with Elixir) and how all of this turned into a project where dozens of folks contributed solutions in dozens of languages.
If you don’t care about the details, here’s the repo with all of the solutions. Originally it was hosted with a different name but Jose renamed it so it doesn’t match the video.
# The Problem Came from a Feature I Was Building
The use case was around developing a table of contents feature for my upcoming custom course platform.
I wanted to output a list of sections
and each section has lessons
, you
could say a section has many lessons. Each section and lesson has its own
position
field too.
The idea is the position
allows us to define a custom order to display
everything in.
Also, the lesson’s position is something that can be toggled to get reset back to 1 for each section, or continue climbing up such as going from lesson 1 to 100 even if there’s 10 sections.
That’s because in the app I was using the same code to render 2 different styles of table of contents and the only difference between them was resetting the lesson position or not.
Input and expected output
The best way to describe it is to provide the input and output. Here’s a bit of dummy data in JSON format. It being JSON isn’t important, but this is the overall shape of the data.
Input
[
{
"title": "Getting started",
"reset_lesson_position": false,
"lessons": [
{"name": "Welcome"},
{"name": "Installation"}
]
},
{
"title": "Basic operator",
"reset_lesson_position": false,
"lessons": [
{"name": "Addition / Subtraction"},
{"name": "Multiplication / Division"}
]
},
{
"title": "Advanced topics",
"reset_lesson_position": true,
"lessons": [
{"name": "Mutability"},
{"name": "Immutability"}
]
}
]
Output
[
{
"title": "Getting started",
"reset_lesson_position": false,
"position": 1,
"lessons": [
{"name": "Welcome", "position": 1},
{"name": "Installation", "position": 2},
]
},
{
"title": "Basic operator",
"reset_lesson_position": false,
"position": 2,
"lessons": [
{"name": "Addition / Subtraction", "position": 3},
{"name": "Multiplication / Division", "position": 4}
]
},
{
"title": "Advanced topics",
"reset_lesson_position": true,
"position": 3,
"lessons": [
{"name": "Mutability", "position": 1},
{"name": "Immutability", "position": 2}
]
}
]
The only change here is a new position
field has been added and it counts
upwards from 1 and continues going up unless a specific section
has the
reset_lesson_position
attribute set to true
in which case the lesson count
starts back at 1, we can see that in the third section.
As a quick aside, for the sake of isolating the problem we decided to propose
the problem by having reset_lesson_position
be configurable on every
section
.
In practice I don’t have that – instead everything is called through a
function and the reset_lesson_position
argument controls resetting all lesson
positions or not for each section. You don’t need to worry about that part for
the sake of this problem.
If you want to take a shot at solving this on your own without spoilers, feel free to give it a whirl and post your solution in a gist in the comments below!
# Preparing to Ask for Help
As someone who is new to functional programming this turned out to be difficult for me to solve in Elixir because my brain isn’t used to working with things like map / reduce, accumulators and all of that.
Whenever I get stuck in spots like this I like to write code in Python since it’s my go-to language. I was able to solve it using Python in about 1 minute.
The Python solution I came up with looked like this:
sections = ... # the JSON data from above as a Python dictionary
section_counter = 1
lesson_counter = 1
for section in sections:
if section["reset_lesson_position"]:
lesson_counter = 1
section["position"] = section_counter
section_counter += 1
for lesson in section["lessons"]:
lesson["position"] = lesson_counter
lesson_counter += 1
print(sections)
To my imperative brain this is the most straight forward thing ever. Keep track of both counters and while looping over the sections if we need to reset the lesson counter then do it up.
Then loop over what we need to and increment both counters. I’m not bragging or anything but this implementation didn’t really require thinking about it. It came out naturally after looking at the data structures I was working with.
I spent more time typing the characters in my code editor than the algorithm.
# Asking for Help
I spent something like 2 hours on my own trying to solve this problem in Elixir. That was a combination of Googling, reading the docs, checking as many resources as I could while playing around with the code in a dozen different ways.
It felt like I hit a dead end so I asked for help on IRC and José (the creator of Elixir) helped me out by giving me a very in depth lesson on one way it could be solved with Elixir.
He stepped me through how he would do it and that code can be found here.
Huge shout out to José for spending ~30 minutes of his time teaching me directly on IRC.
Plot twist
This conversation happened about a year before all of this came together (the upcoming repo we’re about to talk about, this blog post, etc.). A completely separate topic came up recently which resulted in bringing up this use case and code example again.
It ended up being an interesting problem because this type of problem isn’t that friendly towards functional languages but it’s easy with an imperative language. Trade offs!
# Asking the Community How They Would Solve It
That lead to José coming up with a great idea to ask an open question to all developers on how they would solve the problem using their programming language of choice.
We decided to team up on this and he created a repo to accept submissions and then he tweeted out the request for submissions. Then we spent a few days reviewing and merging in around 100 different solutions across 50+ languages.
I was super excited by the turn out. We had something like 20 submissions within the first hour of his tweet and then they continued to roll in over a few days – even getting submissions from languages like APL and Wenyan!
We wanted to limit submissions to being unique in the sense that there could be 5 Elixir solutions but all 5 should use a different way to solve the problem, not just be a slight variant of a variable name, etc..
By the way, there hasn’t been a Brainfuck submission yet so if you’re feeling up to the challenge, please submit a pull request!
But why?
José’s tweet sums that up. He wanted to see how other functional languages besides Elixir would solve the problem.
I think it was a really cool experiment and was worth the time to put it together. I’m all for practical examples and real world use cases and now this repo serves as a way for anyone to see how to implement 1 specific feature in 50+ different languages.
It’s also an excellent example at showcasing how different each of us really are and how we think about how to solve specific problems. There were both imperative and functional solutions provided in languages that support both styles.
How would you solve this problem in your language of choice? Let me know below.