Using Depth-First Search to Transform Nested QuickBooks Data - And Why DSA Isn't Just for Interviews

Vyshnav S Deepak
โš™๏ธ Engineering & Development
Algorithms
Data Transformation
API Integration
FinTech
TypeScript

When most developers hear "DFS" (Depth-First Search), they picture whiteboard interview questions about binary trees or graphs.

But classic data structures and algorithms (DSA) often shine brightest in real-world engineering problems โ€” sometimes in places we don't expect.

At Creditflow, we faced exactly such a scenario while building a financial data integration layer with QuickBooks. Let's explore how a textbook algorithm became the simplest, cleanest solution to a real business need.

๐Ÿงฉ The Problem: Deeply Nested Data

QuickBooks' Balance Sheet API doesn't return a flat list of account balances.

Instead, it mirrors real-world accounting structure: nested groups like:

  • Assets โ†’ Current Assets โ†’ Bank Accounts โ†’ Individual accounts
  • Liabilities โ†’ Current Liabilities โ†’ Credit Cards โ†’ Individual cards

Here's a simplified JSON snippet illustrating what the API might return:

{
  "Rows": [
    {
      "type": "Section",
      "Header": { "value": "Assets" },
      "Rows": [
        {
          "type": "Section",
          "Header": { "value": "Current Assets" },
          "Rows": [
            {
              "type": "Data",
              "ColData": [
                { "value": "XYZ Bank" },
                { "value": "15000" }
              ]
            },
            {
              "type": "Data",
              "ColData": [
                { "value": "ABC Savings" },
                { "value": "8000" }
              ]
            }
          ]
        }
      ]
    }
  ]
}

In real data, this nesting can go even deeper: custom groups, tax details, or other accounting adjustments.

โš™๏ธ What We Needed

Our downstream services โ€” like cash forecasting and reporting dashboards โ€” wanted data as a flat list:

  • Each row: account name + balance
  • Plus the full path (Assets โ†’ Current Assets) so the UI could rebuild the same nested view

Example target output:

๐Ÿ“Š Flattened Data Structure:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Path                     โ”‚  Account      โ”‚  Amount             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Assets โ†’ Current Assets  โ”‚  XYZ Bank     โ”‚  15,000             โ”‚
โ”‚  Assets โ†’ Current Assets  โ”‚  ABC Savings  โ”‚  8,000              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Or in a more hierarchical view:

Assets
โ””โ”€โ”€ Current Assets
    โ”œโ”€โ”€ XYZ Bank: $15,000
    โ””โ”€โ”€ ABC Savings: $8,000

๐Ÿง  The Cleanest Solution: DFS with Path Preservation

At its core, this problem is:

Traverse every node in a tree, remember the path you took, and emit a flat record at each leaf.

That's exactly what Depth-First Search (DFS) does naturally.

โœ๏ธ Pseudo-code (Standard DFS Style)

procedure DFS(node, path, output)
    if node.type == "Section" then
        // Add this section name to path
        path.append(node.Header.value)

        // Recurse into child nodes
        for each child in node.Rows do
            DFS(child, path, output)
        end for

        // Backtrack: remove section name
        path.removeLast()

    else if node.type == "Data" then
        // Extract data
        accountName โ† node.ColData[0].value
        amount โ† node.ColData[1].value

        // Record current path + data
        record โ† {
            path: copy(path),
            account: accountName,
            amount: amount
        }
        output.append(record)
    end if
end procedure

โœ… What Makes This Powerful

  • Unknown depth? DFS handles it: no need to hardcode how many levels
  • Path preserved: we remember the route we took, so the UI can re-create the hierarchy
  • Simple to extend: if QuickBooks adds new nested sections tomorrow, the traversal still works

๐Ÿ“ฆ The Final Transformed Data

[
  {
    "path": ["Assets", "Current Assets"],
    "account": "XYZ Bank",
    "amount": "15000"
  },
  {
    "path": ["Assets", "Current Assets"],
    "account": "ABC Savings",
    "amount": "8000"
  }
]

From here, the frontend can:

  • Build collapsible tree views
  • Group by any level (e.g., totals per section)
  • Dynamically render unknown categories

๐Ÿš€ TypeScript Implementation

Here's the actual code we used:

interface FlattenedRecord {
  path: string[];
  account: string;
  amount: string;
}

function dfsTraverse(
  rows: QuickBooksRow[], 
  currentPath: string[], 
  output: FlattenedRecord[]
): void {
  for (const row of rows) {
    if (row.type === "Section" && row.Header?.value) {
      // Add section to path
      currentPath.push(row.Header.value);
      
      // Recurse into children
      if (row.Rows) {
        dfsTraverse(row.Rows, currentPath, output);
      }
      
      // Backtrack
      currentPath.pop();
    } else if (row.type === "Data" && row.ColData) {
      // Extract leaf data
      output.push({
        path: [...currentPath], // Copy current path
        account: row.ColData[0]?.value ?? "Unknown",
        amount: row.ColData[1]?.value ?? "0"
      });
    }
  }
}

๐Ÿš€ Why This Is More Than Just "Interview DSA"

It's common to hear engineers say:

"DSA is just for cracking coding interviews."

But here, DFS wasn't about passing an interview:

  • It made the data transformation cleaner and safer
  • It handled complexity (unknown nesting) without messy nested loops
  • It kept the code maintainable even as the data model changed

This is what real-world DSA looks like: Algorithms helping us reason about data, write correct code, and future-proof systems.

๐Ÿงก Closing Thought

When we learn algorithms, we often see them on toy problems.

But keep them in your toolkit โ€” because one day, you'll run into messy real-world data, and a textbook algorithm might turn out to be the simplest, most elegant solution.

The next time someone says "DSA is just for interviews," you can point to this: a classic traversal algorithm solving a real business problem, making code cleaner, and keeping systems maintainable.

That's the true power of understanding your fundamentals.


Have you used classic algorithms to solve real-world problems? I'd love to hear your stories โ€” reach out on Twitter or LinkedIn.

ยฉ 2024 Vyshnav S Deepak