[HN Gopher] Fake Trees: Using Indents for Simpler UIs
       ___________________________________________________________________
        
       Fake Trees: Using Indents for Simpler UIs
        
       Author : ingve
       Score  : 172 points
       Date   : 2023-12-30 13:35 UTC (9 hours ago)
        
 (HTM) web link (ratfactor.com)
 (TXT) w3m dump (ratfactor.com)
        
       | philipov wrote:
       | But what do you do if the child needs to refer to the parent, or
       | the parent needs to know its children. An illusion of a tree is
       | only useful insofar as you don't really care about it being a
       | tree.
       | 
       | I structure things as trees because I want to be able to traverse
       | them. When a user sees your tree and expects to be able to do
       | tree operations on it, but discovers its just an illusion and
       | they can do no such thing, then dissatisfaction sets in.
        
         | Semaphor wrote:
         | They mention it a lot, and again at the end in the cautionry
         | section:
         | 
         | > And for the love of all that is good, if you actually need a
         | tree, use a tree.
         | 
         | It's in almost every section.
        
           | kroolik wrote:
           | Its only in the conclusion, though.
        
             | adrianmonk wrote:
             | "Fake" is literally the first word of the title. If a fake
             | version of something had all of the qualities of the real
             | thing, then it wouldn't be fake. It would be real.
             | 
             | Also, it's mentioned at least three other times in the
             | article before the conclusion. Just one example: "Do your
             | items actually have to have a parent-child relationship, or
             | do they just need to look like they do?" That's pretty
             | clear.
        
               | kroolik wrote:
               | The article describes fake trees thus Fake in the title,
               | yes. The article also asks the question you quote, true.
               | 
               | But it also goes on with multiple examples that support
               | their approach whilst neglecting storing data as real
               | tree ("That sounds like a lot of work and potentially
               | awkward-to-work-with data structure, especially if stored
               | in a database" and "one way to get tree-structured data
               | from a relational database with a SQL query is to write a
               | recursive CTE (Common Table Expressions), which are just
               | as fun as they sound"). You don't prove your point by
               | using the most extremely examples of the "bad" approach.
               | 
               | The article is very vocal about shortcomings of storing
               | trees as trees, but quiet about possible issues with the
               | approach used.
               | 
               | It looks more like a emotional rant rather than an
               | informative piece.
        
               | coldtea wrote:
               | > _But it also goes on with multiple examples that
               | support their approach whilst neglecting storing data as
               | real tree_
               | 
               | That's the whole purpose of the article. To _neglect_
               | storing data as a real tree, hence the _fake_ in the
               | title.
               | 
               | It couldn't be more clear about it.
               | 
               | > _It looks more like a emotional rant rather than an
               | informative piece._
               | 
               | It gives descriptions and examples of several techniques,
               | which will do fine for many simple use cases. Which is
               | exactly what it promises.
               | 
               | This reaction seems more like an emotional rant rather
               | than TFA.
        
         | tleb_ wrote:
         | Then use a tree if you need a tree?
        
         | coldtea wrote:
         | > _But what do you do if the child needs to refer to the
         | parent, or the parent needs to know its children._
         | 
         | In the namespacing example, it's trivial: the parent is the
         | version of the name of the current node with one "namespace"
         | less. E.g. /bar/boop/bleep's parent is /var/boop.
         | 
         | Similarly, the children of /bar are any nodes starting with
         | "/bar/".
        
       | ramijames wrote:
       | The problem here is that most often the value in the structure is
       | the hierarchy of the data and NOT the displayed tree. You will
       | want to do stuff with the data like traverse it, show
       | relationships, reorder it, etc.
       | 
       | IMO, it's a dangerous thing to hold VISUAL information in a data
       | structure in a database. Seems shortsighted.
        
         | tomtomistaken wrote:
         | I have recently been faced with a challenge where I need to
         | arrange the top level hierarchy in descending order while
         | simultaneously arranging the second level hierarchy in
         | ascending order with using such structure.
        
         | civilized wrote:
         | It's kind of a strange response when the author already
         | explicitly said "people think they always need to formally
         | encode parent-child relationships but sometimes they don't,
         | sometimes nested display is all that's needed".
         | 
         | What's the response here? "No, that can't possibly be true"?
         | 
         | You Aren't Gonna Need It is a celebrated building heuristic for
         | a reason. You Should Always Assume You Need It simply isn't
         | true.
        
           | tadfisher wrote:
           | You're Gonna Write A Migration For This When the PM Asks For
           | Batch Operations On Subtrees isn't as catchy, I suppose.
        
             | nerdponx wrote:
             | I worked with some YAGNI zealots and this was a routine
             | experience. I adopted the philosophy of IYHTAYPGNI, "If You
             | Have To Ask, You're Probably Gonna Need It".
        
             | NAR8789 wrote:
             | Will you really need a migration for that? OP's storage is
             | already in depth-first order, so subtrees are easy to
             | fetch.
        
           | moron4hire wrote:
           | There's YAGNI and then there's Suitable for Purpose. If you
           | display a thing like a tree, then people are going to expect
           | to do tree-like things to it, like collapse views and reorder
           | items. You might as well just git gud at tree operations and
           | save yourself the hassle once and for all of chasing down
           | edge cases every time a new feature request comes in for
           | things that should have been natural extensions but you
           | omitted "cuz YAGNI".
        
             | civilized wrote:
             | Just because something has a tree structure, it needs to be
             | able to support all tree operations? Why is that such a
             | compelling argument to you? What if it just doesn't?
        
               | setr wrote:
               | It's more like: if something has a tree structure, and
               | the project scope is not 100% finalized, then more likely
               | then not tree operations are going to eventually be
               | requested. The cost savings from the hack is so low
               | versus a standard tree representation that it's unlikely
               | to be worth the risk this occurs
        
               | civilized wrote:
               | The cost savings is low but the risk is high? How do you
               | figure? It sounds like both options are easy (one is just
               | even easier) so there's not much risk if we have to
               | switch.
        
               | moron4hire wrote:
               | The thing is, once you learn how to do tree operations,
               | they aren't that hard to do.
               | 
               | I'm not talking about learning how to balance a binary
               | tree in the most efficient way possible. I'm talking
               | about the basic ops of breadth- and depth-first
               | traversals of arbitrary trees.
               | 
               | I feel like we live in an era of software development
               | that took a stupid meme about "inverting a binary tree"
               | as a job interview question and--not knowing anything
               | about trees--turned it into received wisdom that "trees
               | are hard".
               | 
               | But they just aren't. And yet so many people live in fear
               | of them and go to great lengths to avoid having to learn
               | how to use them.
        
         | ysavir wrote:
         | The irony is that they're still using parent IDs in their data.
         | Instead of storing it in a dedicated column with an optimized
         | data type, it's being prepended to the data string. It might
         | not be a number, and it might not be in an ID column, but it
         | remains an identifier that points to another (expected) value.
         | The change of format doesn't disqualify it from being a parent
         | ID.
        
           | moron4hire wrote:
           | Exactly. What happens if "banana" gets deleted from the
           | database, but not "banana.eat"?
        
         | xg15 wrote:
         | You should be able to easily reconstruct the parent/child
         | relationships from the OP's order/indent encoding scheme though
         | (at least if you ensured that only "valid" indents are stored,
         | no child without a parent, etc)
         | 
         | So I think the easiest way would be to store as order/indent
         | first and later migrate to parent/child when you implement
         | features that need it.
         | 
         | The only change I'd do is to define "indent" more abstractly as
         | the item's depth in the tree and not literally the number of
         | spaces you want to render. That makes it a lot easier to spot
         | invalid data, makes later migration easier - and also gives you
         | more UI flexibility, because it allows you to change the
         | rendering method or make it configurable per user (nested
         | <ul>'s/<ol>'s, tabs, 8 spaces, 4 spaces, 1 space, etc)
        
         | klysm wrote:
         | Strongly agree. Denormalizing data can sometimes be a good
         | idea, but I don't consider this to be a reasonable
         | justification.
        
         | overgard wrote:
         | If you have a data structure like...                   struct
         | item_t {            char key[255];            char
         | display_value[255];         }
         | 
         | And your key has consistent path separators like a/b/c
         | 
         | Then looking up parents and children is incredibly easy. At
         | worst it's a linear scan of the array, but if it's in order you
         | can even just look at previous items in the list until you're
         | at the parent.
        
       | gavinray wrote:
       | Postgres has a datatype and search operators that work natively
       | like this:
       | 
       | https://www.postgresql.org/docs/current/ltree.html
       | 
       | For instance:                 CREATE TABLE test (path ltree);
       | INSERT INTO test VALUES ('Top');       INSERT INTO test VALUES
       | ('Top.Science');       INSERT INTO test VALUES
       | ('Top.Science.Astronomy');
       | 
       | And then a simple search with:                 ltreetest=> SELECT
       | path FROM test WHERE path <@ 'Top.Science';
       | path       ------------------------------------
       | Top.Science        Top.Science.Astronomy
        
         | earthboundkid wrote:
         | Do you know if the divider can be a / for storing file paths?
        
           | halfmatthalfcat wrote:
           | No, it must be stored as a "." for ltree. However, nothing is
           | stopping you from converting the "." to a "/" after it's
           | pulled from the db.
        
             | okasaki wrote:
             | The majority of file paths will include at least one '.'
        
               | kayson wrote:
               | You could swap all / for . and . for /
        
               | gkbrk wrote:
               | Just escape them.
        
         | Phelinofist wrote:
         | Any experience with performance? This seems to be heavy with
         | regexes
        
           | coldtea wrote:
           | Where do you see the regexes?
        
           | barrkel wrote:
           | This kind of encoding is usually efficient when the column is
           | indexed because it can use prefix searches.
        
         | Lariscus wrote:
         | SQL Server has something very similar[1]. It works quite well
         | in my experience.
         | 
         | [1] https://learn.microsoft.com/en-us/sql/relational-
         | databases/h...
        
         | qudat wrote:
         | This works well until you need to preserve children order then
         | it doesn't quite work without further mods
        
           | michaelsalim wrote:
           | Then add a sort column just like any other implementation.
           | Even the example given by the article uses it.
        
             | qudat wrote:
             | As far as I remember it isn't that simple. Moving
             | operations now require extra queries and reshuffling of
             | order. Doable, just far less elegant for a very common use
             | case.
        
           | tejtm wrote:
           | Yes.
           | 
           | In case you ever work with an Anatomist, and they are visibly
           | freaking out at you destroying everything it is because they
           | order parts anterior to posterior and it is sooooooo obvious
           | they wont ever feel the need to mention it ...
        
         | nextaccountic wrote:
         | Can the same be done with json columns? It would enable using
         | datatypes other than strings for the nodes
         | 
         | A concern is that json indexes might not work as well as ltree
         | indexes
        
       | earthboundkid wrote:
       | Another way to make a fake tree is to store a JSON blob. If the
       | data only has internal relationships, that can be easier than
       | trying to keep the sort number unique and in order.
        
         | Retr0id wrote:
         | Arguably a nested JSON representation of a tree is even more
         | "real" than the virtual tree you get when you store parent
         | references in a db.
        
       | leeoniya wrote:
       | i like the MPTT / nested set model to store trees in tables.
       | 
       | https://en.m.wikipedia.org/wiki/Nested_set_model
       | 
       | https://stackoverflow.com/questions/5368299/hierarchical-dat...
       | 
       | this can be used to make these fake trees while avoiding the
       | adjacency structure that's harder to query.
        
       | jmvoodoo wrote:
       | I started a company that dealt with a lot of tree like data. It
       | is possible to transform your tree structure into an indented
       | list in O(n) time. This used to be one of our interview questions
       | at the time. There are a number of ways to store your data in
       | various SQL databases that allow you to quickly get and render
       | segments of the tree as well without recursive queries.
       | 
       | Once you understand those concepts, then storing your data
       | correctly as trees has a ton of benefits over indenting like
       | this.
        
         | sfn42 wrote:
         | If you don't need those benefits it doesn't really matter.
        
           | moron4hire wrote:
           | What people are saying in this comment section is that you're
           | probably going to need it. You might not need it now, but the
           | PM of today is a short-sighted person and the future always
           | gets here.
        
       | renewiltord wrote:
       | Indenting for pseudo tree handling. The first time I saw that
       | trick was on HN. View the source of this page to see.
        
       | aragonite wrote:
       | > I learned the hard way long ago: more often than not, people
       | don't actually want (or need) trees, they just need the
       | appearance of them.
       | 
       | I've been noticing that this is one way in which HN and Reddit
       | differ. On HN, a child comment is a nextSibling of the parent
       | comment, with indent set to parent's indent plus one to simulate
       | the appearance of trees. On Reddit (or at least old.reddit.com,
       | don't know about the new site), a child comment is actually
       | nested inside the parent comment.
        
         | pvg wrote:
         | You mean in the HTML, not in the actual presentation, right?
         | The way it looks on screen is more or less identical.
        
           | hunter2_ wrote:
           | That's definitely how I interpret it, as using the word
           | nextSibling [0] in camel case is quite specific to DOM
           | manipulation.
           | 
           | While "the way it looks" might be all that matters to most
           | users, perhaps there are cases where semantic markup is
           | critical (screen readers? browser extension development?).
           | 
           | [0] https://developer.mozilla.org/en-
           | US/docs/Web/API/Node/nextSi...
        
         | jprete wrote:
         | I can't imagine it's stored this way in the backend. Every
         | operation on the data would be a complicated mess of inferring
         | the tree structure then translating back to the implied tree
         | format.
        
         | sterlind wrote:
         | how does collapse work then?
        
           | kijin wrote:
           | Loop over next siblings and hide them one by one, until you
           | hit an entry with an indentation level that is less than or
           | equal to that of the current entry?
        
           | refset wrote:
           | Take a look at the kidvis function in hn.js
        
           | archgoon wrote:
           | It iterates across sibling comment elements, hides them,
           | until it encounters one whose indent level is less than or
           | equal to the initial indent level.
        
             | sterlind wrote:
             | that makes sense. you're shadowbanned by the way, I had to
             | vouch for your comment.
        
       | jchw wrote:
       | This reminds me of the MPTT (Modified Pre-order Tree Traversal)
       | method used in a lot of older SQL-based software, like phpBB (and
       | no doubt still used today.) I won't attempt to explain exactly
       | how it works, but it stores a tree structure in a way that makes
       | many kinds of queries possible even when treating the table more
       | or less like a flat list. It was very useful for being able to
       | snag subtrees in relatively simple SQL queries.
       | 
       | Also, tangentially, I am reminded of the array representation of
       | the binary heap structure. An array-based binary heap maintains
       | (as far as I am aware) the same properties as a pointer-based
       | binary heap, but the structure is entirely implicit based on
       | index.
       | 
       | There's a lot of ways to skin a cat for sure. Reaching for
       | pointer-based trees of objects is of course totally fine, but
       | there's a lot of ways to normalize data, and if you have a decent
       | idea of what you're looking for you can definitely save a lot of
       | trouble.
        
       | parasti wrote:
       | I had a similar realization about OpenGL a bunch of years ago: I
       | don't need to draw a world of hierachical 3D objects, I just need
       | to draw a list of sorted triangles. That flipped a switch in my
       | head and made a bunch of optimizations very easy.
        
         | whstl wrote:
         | True, simplicity goes a long way in post-2000 3d games. Even in
         | games where there's complex entity hierarchies, it often has to
         | be collapsed into a flat structure when added to render queue
         | (for things like transparency sorting). "Flat lists of things"
         | is also pretty much also the foundation of ECS/DOD.
        
       | g8oz wrote:
       | "one way to get tree-structured data from a relational database
       | with a SQL query is to write a recursive CTE (Common Table
       | Expressions), which are just as fun as they sound."
       | 
       | I promise you that CTEs, even recursive ones, are not scary and
       | that once you get the hang of them _are_ actually fun.
        
         | fs_tab wrote:
         | Yes, CTEs are fine. The author could have used one to generate
         | a view containing formatted names instead of baking this
         | information into the table.
        
         | forgetfulness wrote:
         | I don't find CTEs particularly fun, copying and pasting the
         | whole tower of CTEs into another SQL window to debug the one
         | I'm interested in is decidedly not a form of entertainment I
         | pursue.
        
           | barrkel wrote:
           | You only need one recursive CTE typically and you can encode
           | it into a view that you join against. It just sucks for
           | performance when you have millions of nodes.
        
         | barrkel wrote:
         | I found using recursive CTEs incredibly slow for assembling
         | tree data from a normalized representation. It would make
         | getting the results of a query at least d times slower for
         | assembling the path of nodes d deep in the hierarchy. The
         | upside was cheap tree edit operations but those were a lot less
         | frequent than reads.
        
       | iLoveOncall wrote:
       | This twists a problem to make the solution fit.
       | 
       | Nobody would store "Foo 1 a" as name of the deepest item, they
       | would want to build the name dynamically based on the
       | concatenation of the parents "Foo" and "1", in which case the
       | system doesn't work at all.
       | 
       | Otherwise, most operations on the structure are impossible to
       | perform (a simple rename in the middle of a branch).
       | 
       | Disingenuous article.
        
         | kroolik wrote:
         | Agree. The article is imho clickbaity and very shallow in the
         | reasoning presented.
        
         | pflanze wrote:
         | The sorting is according to the "sort" column, not the "name"
         | column (they are not explicit about this but it's clearly what
         | they mean, and it solves your problem).
         | 
         | I think the benefit could mostly be for the implementation of
         | the GUI to change the tree (that they do mention).
         | 
         | If you don't feel like using a hack, don't. (They make that
         | clear, too.)
        
       | douglee650 wrote:
       | Isn't this just syntactic sugar of parent-child-relationships?
        
       | kroolik wrote:
       | The idea behind the article is simple - use the structures fit
       | for the problem.
       | 
       | The narrative, though, is imho faulty. You dont need CTE to
       | retrieve the tree from the db - you can fetch a flat list and
       | construct the tree locally, as you would most likely need to
       | anyway for following manipulations.
       | 
       | The very same argument can be made about people who use rdbms to
       | store the list - store it in a text file. Why pay the network
       | latency cost?
       | 
       | Or alternatively, the proposed structure would not work with
       | sufficiently big tree if we wanted to move branches around,
       | changing their depth. That would impose linear cost.
       | 
       | State your intention at the beginning, instead of explaining
       | three different examples that are later on invalidated in the
       | conclusion section with "if you need a tree, use a tree". But
       | adding this to the beginning of the article would be way less
       | clickbaity.
        
       | SloopJon wrote:
       | Although this post doesn't establish the premise sufficiently to
       | comment on the data or its schema, I would venture that much of
       | the time what you want is a GROUP BY query on columns of the
       | table, rather than a column of pointers.
       | 
       | What distinguishes a Foo from a Bar? Are 1, 2, and 3 (or I, II,
       | etc.) actual data values, or simply row numbers arising from a
       | sort on something else?
       | 
       | If you're writing the back end for OmniOutliner, and you must use
       | a relational database, then maybe you'll use tricks like these.
       | Otherwise, I would see whether you can come up with a schema that
       | better models your data.
        
       | selfawareMammal wrote:
       | A green plastic watering can...
        
         | quickthrower2 wrote:
         | For her fake Chinese rubber plant
         | 
         | In the fake plaaaaaaaaaaaaaaaaaaaastic earth
        
       | rubymamis wrote:
       | Funny, I'm using the same technique in my block editor[1]. Rather
       | than using the complex table/tree-like data structure of
       | QAbstractItemModel[2], I'm using QAbstractListModel[3] (flat
       | list) with a property called indentLevel[4] for each delegate. It
       | works out great. I still need to manage parent child
       | relationships (it makes rendering changes easier using recursive
       | function) so I keep variables for that, but the "fake tree" look,
       | as I like to call it, simply uses the indentLevel property which
       | is determined by the amount of whitespace/tabs at the start of a
       | line in the underlying plaintext data.
       | 
       | [1] https://www.get-plume.com/
       | 
       | [2] https://doc.qt.io/qt-6/qabstractitemmodel.html
       | 
       | [3] https://doc.qt.io/qt-6/qabstractlistmodel.html
       | 
       | [4] Q_PROPERTY(unsigned int indentLevel READ indentLevel WRITE
       | setIndentLevel NOTIFY indentLevelChanged)
        
       | junek wrote:
       | I would be _very_, very cautious with this. The problem is
       | precisely that the system presents itself in a way that implies
       | more than it actually has going on. Users will naturally assume
       | there is a real parent-child relationship in the underlying data
       | model.
       | 
       | A soon as someone asks for <thing that requires the parent-child
       | relationship that is implied to already be there>, then you have
       | to admit that it's a smoke-and-mirrors trick and you can't
       | actually achieve that with the data you have.
       | 
       | Avoid storing visual figments in your database, they'll come back
       | to haunt you.
        
       | danielvaughn wrote:
       | At first glance, this just seems like an adjacency list, am I
       | missing something?
        
       | twic wrote:
       | The first approach (the 'It's "obviously" the only way to go'
       | one) is called an adjacency list.
       | 
       | The second (the 'vastly simpler method') i don't recall seeing
       | before. It has some fairly obvious deficiencies, but it is
       | clearly enough in some cases.
       | 
       | The third ('namespacing') is called a materialized path.
       | 
       | And there is at least another way to represent trees - nested
       | sets:
       | https://www.ibase.ru/files/articles/programming/dbmstrees/sq...
       | 
       | All of these were well-trodden back in the days when people took
       | relational databases seriously. For example, see:
       | http://www.dbazine.com/oracle/or-articles/tropashko4/
       | 
       | It seems this is lost knowledge now.
        
         | strontian wrote:
         | Thank you, this type of info is rare to come across and much
         | appreciated
        
         | crgk wrote:
         | I love this comment. One of the worst feelings at my old job
         | was putting a ton of effort into describing a problem, only for
         | somebody else to recognize it as a known, studied thing that
         | already has a name.
         | 
         | It's really hard, in my experience, to come across the existing
         | name for something while you're still figuring out all the
         | angles of a problem for yourself.
        
           | auselen wrote:
           | I'm opposite about the feeling.
           | 
           | When I work on a problem that's new to me, I ask around,
           | explaining the problem, checking if someone recognizes the
           | domain, if it is known.
           | 
           | When someone explains to me what I'm working on is a solved
           | problem, I take great joy from it since I already understand
           | the issue some, I can criticize or take great energy from
           | learning something for sure.
           | 
           | I believe how to feel about it is a choice btw. As movie says
           | "Always Look on the Bright Side of Life".
        
           | ShadowBanThis01 wrote:
           | I don't know why you should feel bad about that. If anything,
           | you should feel good that you independently recognized a
           | problem so significant that it was given a name.
           | 
           | I assume most problems I face are solved, and that I just
           | don't know shit. So I scramble to research the solutions, and
           | I'm shocked when what seem to be widely-encountered problems
           | are NOT, in fact solved.
           | 
           | One recent example I encountered was API definition. I was
           | tasked to define a new API for my company's products, and
           | celebrated when I discovered OpenAPI. I was a bit surprised
           | to find that only the very latest version of the standard
           | (3.1) was competent enough to be useful.
           | 
           | And despite 3.1 being ratified for years, today there are
           | still no usable code-generation tools that support it. I
           | wasted weeks studying and trying to fix various tools, after
           | studying reams of redundant and conflicting documentation in
           | different repositories... thinking it was my problem. No.
           | It's just a hideously broken mess.
           | 
           | Today I'm dealing with the same thing in SwiftUI... and again
           | have reached the conclusion that the programming paradigm it
           | pushes has not been thought through. Its rushed and immature
           | state shows in its kitchen sink full of overlapping and
           | rapidly-deprecated approaches to problems that were solved in
           | traditional application structures a decade and a half ago.
           | 
           | Just typing that out, I wonder if I failed to learn from my
           | first example and wasted too much time on the second. But if
           | you're a thorough person, you have to satisfy yourself that
           | you've been diligent in trying to inform yourself of best
           | practices.
        
         | ted_bunny wrote:
         | Why do people not take RDBs seriously anymore?
        
           | michaelteter wrote:
           | Prevalence of abstraction layers (ORMs), document stores,
           | key-value stores, etc.?
           | 
           | Someone somewhere decided they didn't like SQL and engineered
           | a way to postpone having to know SQL until their ORM
           | performance dragged their systems to a halt.
        
           | chadcmulligan wrote:
           | I'm thinking performance - it used to be a big deal to
           | optimise database performance. Now most databases can live in
           | memory so even if you're doing full table scans all the time
           | it won't be that noticeable, and by the time the database has
           | enough data in it that performance becomes an issue every one
           | who built it has left.
           | 
           | The focus on micro services to, probably has an impact - the
           | back end can be changed if your database is having issues and
           | the services stay the same.
           | 
           | Also databases are hard and a large chunk of developers only
           | know javascript and html.
           | 
           | You don't see many DBA jobs any more, a lot of that is moving
           | to the cloud so they just pay more money and throw CPU's at
           | the problem rather than optimise the database.
           | 
           | Also agile/scrum (as its practiced anyway) doesn't really
           | allow for a database design up front as it used to be done.
        
       | paradox460 wrote:
       | This trick is particularly useful for rendering things that have
       | somewhat of a hierarchy, but aren't implicitly hierarchal. Like a
       | table of contents for a HTML page, targeting the various header
       | (h1-h6) elements. They're not (generally) descendents of each
       | other, and parsing them is simpler to just go as a list of items
       | with a depth.
       | 
       | I used this for the table of contents on my personal site:
       | 
       | Parser: https://github.com/paradox460/pdx.su/blob/main/lib/toc.ex
       | 
       | Renderer:
       | https://github.com/paradox460/pdx.su/blob/main/lib/layouts/p...
        
       | thom wrote:
       | There's a whole book about doing this in databases, FWIW:
       | 
       | https://www.oreilly.com/library/view/joe-celkos-trees/978155...
        
         | quickthrower2 wrote:
         | Ah and they say all books are for beginners! Nice.
        
       | cuddlyogre wrote:
       | I maintain an old cms that stores the id of the parent, left
       | sibling, and right sibling.
       | 
       | Making any changes to the order or grouping is wrought with peril
       | because you never know if everything is going to be updated
       | properly. When it isn't, it's an ordeal to correct.
       | 
       | Having a parent id and sort order is straightforward and about as
       | simple as it gets. I really don't see why you'd need to do it any
       | other way outside of a thought experiment.
        
         | noizejoy wrote:
         | we used to call that a "linked list"[0]
         | 
         | [0] https://en.wikipedia.org/wiki/Linked_list
        
         | plonq wrote:
         | Thats called Modified Preorder Tree Traversal and the reason to
         | use it over simple parent ID and order is performance.
        
           | cuddlyogre wrote:
           | Thank you for explaining it. I'm curious if it's just a bad
           | implementation.
        
       | overgard wrote:
       | Another underrated benefit is this is potentially a lot faster. A
       | flat array, which you can use to store this, is a lot more cache
       | friendly to a CPU than a data structure with a lot of pointers.
       | (Although this mostly requires a language like C++ where you can
       | control the memory layout)
        
       | quickthrower2 wrote:
       | OneNote does this. I quite like it except you need to use a
       | context menu to change indentation.
        
       ___________________________________________________________________
       (page generated 2023-12-30 23:00 UTC)