Creating hierarchical structure (nesting)

Hi,

I’m new to ReScript (and nearly new to functional programming) and I’m trying to convert a flat data structure into a hierarchical one, but cannot figure out how to do this in ReScript (and would be equally stumped in many other strongly-typed languages). I’m an accountant and like to program in my spare time with this as my new pet project!

I’m trying to model a set of financial accounts and the input is an array of data that looks like:

array<(array<String, String, String, String>, int)>    //option 1
//or
array<(String, String, String, String, int)>    //option 2

for context an element might be:
("Assets", "Non-current assets", "Property", "34 Long Road", 25000000)

and I want to turn this into a nested structure to pass to React for displaying financial statements. The nested structure would look something like this (I’m sure room for improvement):

type rec childType =
| Category(array<account>)
| Account(int)
and account = {name: string, value: int, children: childType}

When using plain javascript I depended on D3.nest (https://github.com/d3/d3-collection#nests) for this purpose, I could depend on this again if needed. However, I’d love solve this in ReScript as I have been struggling for hours. I understand that D3’s nest is weakly-typed, you make assertions about the shape of the data when calling the function, and you get a runtime error if the data does not conform to expectations.

I’ve tried starting with my option 1 above, but I think the problem is the number of Strings in the inner array is not encoded in the type so in a recursive function it is hard to convince ReScript that every element of an array has reached an edge condition at the same time.

I then explored option 2 above, my idea being that a function would be called with (X, X, X, X), then (X, X, X), then (X, X), then (X) recursively, but I think tuples of different sizes are completely different types and I don’t think you can write such a generic function in ReScript. I considered a variant with all four options but that idea exploded with boilerplate and I’d ideally want something that generalised to an arbitrary number of levels of nesting. In C++ I think I could write a recursive template function using parameter packs or fold expressions to achieve this.

How about this? Although I’m not sure about what you’re trying to do, can you share a sketch of the React UI you’re aiming for?

type rec category = array<account>
and account = {name: string, value: int, children: array<category>}

Usage playground here

Note: I think recursive react components are a bit fiddly, not sure if this is the best way to use them. Maybe someone can put me straight here.

Thanks Tom,

My question is really around how to write a nest function rather than how to then deal with it in React. Starting from my option 1 or option 2 I struggle to design a recursive function which will output something nested.

To provide a concrete example (using JSON-ish syntax), I want to start with something like this:
[
("Assets", "Non-current assets", "Property", "34 Long Road", 25000000)
("Assets", "Current assets", "Cash", "JP Morgan", 10000)
("Liabilities", "Non-current liabilities", "Loans", "Bank loan", 25000000)
("Equity", "Share capital", 10000)
]

and output something like this:

[
  {
    "name": "Assets",
    "value": 25010000,
    "children": [
      {
        "name": "Non-current assets",
        "value": 25000000,
        "children": [
          {
            "name": "Property",
            "value": 25000000,
            "children": [
              {
                "name": "34 Long Road",
                "value": 25000000,
                "children": []
              }
            ]
          }
        ]
      },
      {
        "name": "Current assets",
        "value": 10000,
        "children": [
          {
            "name": "Cash",
            "value": 10000,
            "children": [
              {
                "name": "JP Morgan",
                "value": 10000,
                "children": []
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "name": "Liabilities",
    "value": 25000000,
    "children": [
      {
        "name": "Non-current liabilities",
        "value": 25000000,
        "children": [
          {
            "name": "Loans",
            "value": 25000000,
            "children": [
              {
                "name": "Bank Loan",
                "value": 25000000,
                "children": []
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "name": "Equity",
    "value": 10000,
    "children": [
      {
        "name": "Share Capital",
        "value": 10000,
        "children": []
      }
    ]
  }
]

I think such a function will involve something like mapping through the outside array, grouping by the first element (using a Belt.HashMap, perhaps), then converting back to an array and recursively repeating. However, I just cannot fathom how to write such a function.

Having set out the issue above, I’m going to have another go at this today and will of course share results if successful.

(as an aside, I’ve been using Preformatted text for my code snippets and could not see how to add syntax highlighting. I’ve probably missed something obvious)

Accounts are a tree structure, typical way to model them would be:

type rec account = {
  id: string,
  description: string,
  // map of account IDs to accounts
  children: Belt.Map.String.t<account>,
}

In this model there is no distinct ‘category’ concept, everything is an account, and each account can have children or not. So e.g. ‘Assets’ is an account, ‘Non-current assets’ is another account which is a child of ‘Assets’, and so on.

Another big difference would be that you wouldn’t store the ‘value’ of the account directly in the account object itself, you would calculate it from the transactions in the ledger.

I recommend checking Ledger for a good model of double-entry bookkeeping.

1 Like