Skip to content
Astera Technology

Expand a Boolean decision tree into explicit outcome paths

Carry every branch condition to each outcome leaf.

Download complete case notes and answer keys ↓

Source contract and expected records

This is an original, fully synthetic mapping example. Inputs are plain, acyclic JSON-shaped records with the fields shown; the canonical shape and rejection codes belong to this example. Adapt the contract before using a different source.

The original tree has nonempty text outcome leaves or nodes containing if, then and else. if names a Boolean condition. Recursively carry true/false conditions down each branch; reject a missing branch or a field repeated along one path. An unconditional text outcome becomes one path with no conditions.

Editable synthetic mapping cases
CaseInput record (JSON)Expected output (JSON)
Flatten original branch tree{"tree":{"if":"urgent","then":"fast","else":{"if":"special","then":"specialist","else":"normal"}}}{"ok":true,"value":{"paths":[{"conditions":[{"field":"urgent","is":true}],"outcome":"fast"},{"conditions":[{"field":"urgent","is":false},{"field":"special","is":true}],"outcome":"specialist"},{"conditions":[{"field":"urgent","is":false},{"field":"special","is":false}],"outcome":"normal"}]}}
Reject missing branch{"tree":{"if":"urgent","then":"fast"}}{"ok":false,"error":"TREE_NODE"}
Preserve unconditional outcome{"tree":"manual"}{"ok":true,"value":{"paths":[{"conditions":[],"outcome":"manual"}]}}
Download editable CSV ↓

Run the local transformation

Run the complete example as a JavaScript module in Node.js. It contains the pure mapping function, all three inputs and literal expected outputs; assertions stop on a mismatch. A rejected case returns ok=false with an error code, rather than a partial canonical record.

Complete function and executable cases
import assert from 'node:assert/strict';

function decisionPaths(i){
  const fail=error=>({ok:false,error});if(!i||!Object.hasOwn(i,'tree'))return fail('TREE_MISSING');
  const paths=[];let error;
  const walk=(node,conditions,used)=>{
    if(typeof node==='string'){if(!node){error='TREE_OUTCOME';return;}paths.push({conditions,outcome:node});return;}
    if(!node||typeof node!=='object'||Array.isArray(node)||typeof node.if!=='string'||!node.if||!Object.hasOwn(node,'then')||!Object.hasOwn(node,'else')){error='TREE_NODE';return;}
    if(used.has(node.if)){error='TREE_REPEAT';return;}
    const next=new Set(used);next.add(node.if);
    walk(node.then,[...conditions,{field:node.if,is:true}],next);
    walk(node.else,[...conditions,{field:node.if,is:false}],next);
  };
  walk(i.tree,[],new Set());return error?fail(error):{ok:true,value:{paths}};
}

// Original synthetic cases; expected outputs are literal records.
const cases = [
  {
    "label": "Flatten original branch tree",
    "input": {
      "tree": {
        "if": "urgent",
        "then": "fast",
        "else": {
          "if": "special",
          "then": "specialist",
          "else": "normal"
        }
      }
    },
    "expected": {
      "ok": true,
      "value": {
        "paths": [
          {
            "conditions": [
              {
                "field": "urgent",
                "is": true
              }
            ],
            "outcome": "fast"
          },
          {
            "conditions": [
              {
                "field": "urgent",
                "is": false
              },
              {
                "field": "special",
                "is": true
              }
            ],
            "outcome": "specialist"
          },
          {
            "conditions": [
              {
                "field": "urgent",
                "is": false
              },
              {
                "field": "special",
                "is": false
              }
            ],
            "outcome": "normal"
          }
        ]
      }
    }
  },
  {
    "label": "Reject missing branch",
    "input": {
      "tree": {
        "if": "urgent",
        "then": "fast"
      }
    },
    "expected": {
      "ok": false,
      "error": "TREE_NODE"
    }
  },
  {
    "label": "Preserve unconditional outcome",
    "input": {
      "tree": "manual"
    },
    "expected": {
      "ok": true,
      "value": {
        "paths": [
          {
            "conditions": [],
            "outcome": "manual"
          }
        ]
      }
    }
  }
];
for (const sample of cases) {
  const actual = decisionPaths(sample.input);
  assert.deepEqual(actual, sample.expected);
  console.log(sample.label, JSON.stringify(actual));
}
Download runnable example ↓

Work through the boundary

Which conditions reach specialist in the first case?

Show the worked answer

urgent=false followed by special=true. fast requires urgent=true; normal requires both urgent=false and special=false. The three paths describe the supplied tree rather than executing a decision.

Related resources