Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

100+ Ways to Solve a Specific Programming Problem in 50+ Languages

blog/cards/100-ways-to-solve-a-specific-programming-problem-in-50-languages.jpg

José Valim and I asked developers how they would solve a specific problem in their language of choice. How would you do it?

Quick Jump: The Problem Came from a Feature I Was Building | Preparing to Ask for Help | Asking for Help | Asking the Community How They Would Solve 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.

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.

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per month (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.



Comments