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.
A nonempty base label accompanies unique nonempty option keys and values. The local canonical variant recognises colour, finish and size only. Put other options in unmapped rather than discarding them. Repeated keys reject the mapping, including repeated unknown keys.
| Case | Input record (JSON) | Expected output (JSON) |
|---|---|---|
| Map declared option keys | {"base":"A","options":[{"key":"colour","value":"blue"},{"key":"finish","value":"matte"}]} | {"ok":true,"value":{"base":"A","variant":{"colour":"blue","finish":"matte"},"unmapped":[]}} |
| Reject repeated option key | {"base":"A","options":[{"key":"colour","value":"blue"},{"key":"colour","value":"red"}]} | {"ok":false,"error":"VARIANT_DUPLICATE"} |
| Preserve undeclared option | {"base":"A","options":[{"key":"material","value":"paper"}]} | {"ok":true,"value":{"base":"A","variant":{},"unmapped":[{"key":"material","value":"paper"}]}} |
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.
import assert from 'node:assert/strict';
function variantOptions(i){
const fail=error=>({ok:false,error});if(!i||typeof i.base!=='string'||!i.base||!Array.isArray(i.options))return fail('VARIANT_SHAPE');
const variant={},unmapped=[],keys=new Set();
for(const o of i.options){
if(!o||typeof o.key!=='string'||!o.key||typeof o.value!=='string'||!o.value)return fail('VARIANT_VALUE');
if(keys.has(o.key))return fail('VARIANT_DUPLICATE');keys.add(o.key);
if(['colour','finish','size'].includes(o.key))variant[o.key]=o.value;else unmapped.push({key:o.key,value:o.value});
}
return {ok:true,value:{base:i.base,variant,unmapped}};
}
// Original synthetic cases; expected outputs are literal records.
const cases = [
{
"label": "Map declared option keys",
"input": {
"base": "A",
"options": [
{
"key": "colour",
"value": "blue"
},
{
"key": "finish",
"value": "matte"
}
]
},
"expected": {
"ok": true,
"value": {
"base": "A",
"variant": {
"colour": "blue",
"finish": "matte"
},
"unmapped": []
}
}
},
{
"label": "Reject repeated option key",
"input": {
"base": "A",
"options": [
{
"key": "colour",
"value": "blue"
},
{
"key": "colour",
"value": "red"
}
]
},
"expected": {
"ok": false,
"error": "VARIANT_DUPLICATE"
}
},
{
"label": "Preserve undeclared option",
"input": {
"base": "A",
"options": [
{
"key": "material",
"value": "paper"
}
]
},
"expected": {
"ok": true,
"value": {
"base": "A",
"variant": {},
"unmapped": [
{
"key": "material",
"value": "paper"
}
]
}
}
}
];
for (const sample of cases) {
const actual = variantOptions(sample.input);
assert.deepEqual(actual, sample.expected);
console.log(sample.label, JSON.stringify(actual));
}
Work through the boundary
Where does material=paper go when no recognised option is supplied?
Show the worked answer
variant remains {} and unmapped retains the material/paper pair. The output retains base; it does not manufacture a new SKU from the option text.