DEV Community

Adam Smolenski
Adam Smolenski

Posted on

Recursively climb an object to find it's full path.

So recursion can be intimidating, I find myself constantly thinking of numbers and base cases, then figuring it all out from there. APIs and nested data makes me happy... It's like a scavenger hunt. Where's this data and how can I find it. So I came across a fun example of originally trying to find a file path. Given a target file and a file structure can you recreate the path as a string. Which I realized could be extremely useful if you have a JSON object and you're trying to find a specific variable. There are some APIs that are just a huge headache sifting through data. Sort of why I am enjoying GraphQL but that's another story.

For the purpose of this walkthrough and my unimaginative process let's use a file tree. I used to get lost in the Rails file tree all the time so I feel like we can recreate that with some embellishment to make it trickier to find where we are going. Here's how we can represent the path as an object.

let rails = {
    '/app': {
        '/channels': {
            '/application': {
                'channel.rb': null,
                'connection.rb': null
            }
        },
        '/controllers': {
            '/concerns': {
                '.keep': null
            },
            'application_controller.rb': null },
        '/jobs': {
            'application_job.rb': null
        }

    },
    '/config': {
        '/environments': {
            'development.rb': null,
            'production.rb': null,
            'test.rb': null
        }
    },
    '/db': {
        'schema.rb': null,
        'seeds.rb': null,
        '/migrate': {
            'create_tables.rb': null,
            'fix_columns.rb': null
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

If we were to just find that our file is in there that's easy just keep going through each directory an once we find it return true... That would look something like this:

function findFile(structure, target) {
    for (let subdir in structure) {
        if (subdir === target || findFile(structure[subdir], target)) {
            return true
        }
    }
    return false
}
console.log(findFile(rails, 'fix_columns.rb'))
Enter fullscreen mode Exit fullscreen mode

That will return true since fix_columns is somewhere in there... but how can we come to that code in the first place?

Think of the simplest file structure given... just one directory, so let's take

folder = {
            'create_tables.rb': null,
            'fix_columns.rb': null
        }
Enter fullscreen mode Exit fullscreen mode

So here we just need to iterate over the keys and check it against our target file

function findFixColumns() {
    for (let key in folder) {
        if (key === 'fix_columns.rb') {
            return true
        }
    }
    return false
}
console.log(findFixColumns())
Enter fullscreen mode Exit fullscreen mode

So going back to our recursive look, how an || statement works is the left side is evaluated before the right, and will return true if just the left is true. So by diving repeatedly deeper on the right side, by the base case will return true if it has exhausted all options. So that leaves us with our simple, hey it's found on a higher level, "I'm true, get me out of here!" solution. How can we go from there into getting the file path now? Well in Javascript a string is considered true.. so in theory we can work on what we just did and compile a string, instead of just returning true. So we can build on that with two different cases in mind, finding the file and then building on folders.

function findFilePath(structure, target) {
    for (let subdir in structure) {
        if (subdir === target) {
            return `/${subdir}`
        }
        let found = findFilePath(structure[subdir], targetFile)
        if (found) {
            return `${subdir}${found}`
        }

    }
    return null
}

Enter fullscreen mode Exit fullscreen mode

So here, our basecase is finding the target file. It will return a string, which will translate to true. If that isn't the top level, we will continue on in our function and search the filepath in the subdirectories, since found is our basecase return we build off of that and apply our subdir which is the directory it is found in... so when we try this for our fix_columns.rb friend, the return would be:
/db/migrate/fix_columns.rb.
Woooo, we've recursively found our file by figuring out the base case and building up.

For me these word problems seem way less stressful than dealing with numbers and fibonacci but that may be my love of puns. Not all recursion is scary. One of my favorite recursive functions for a production app was with the Eventbrite API being very cranky and only giving 50 results at a time when I needed 200... So recursively called and added to an array... such fun times. APIs have so much data that manipulating it into what you want may be stressful, but just think of it as wordplay and you might just enjoy it.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • 0:56 --last-failed: Zero in on just the tests that failed in your previous run
  • 2:34 --only-changed: Test only the spec files you've modified in git
  • 4:27 --repeat-each: Run tests multiple times to catch flaky behavior before it reaches production
  • 5:15 --forbid-only: Prevent accidental test.only commits from breaking your CI pipeline
  • 5:51 --ui --headed --workers 1: Debug visually with browser windows and sequential test execution

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Click on any timestamp above to jump directly to that section in the tutorial!

Watch Full Video 📹️

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay