Generating Variant Matrices Without Overwriting Form Data

Algorithms · May 12, 2026 · 5 min read

If you recalculate combinations from scratch every time an attribute changes, your users will hate you. Here is how state reconciliation keeps existing prices intact.

If you have ever bought a laptop online, you know the drill: you pick 16GB or 32GB RAM, 512GB or 1TB SSD, Space Grey or Silver.

Behind the scenes in the store's admin panel, someone had to configure the price, stock, and SKU for every single one of those combinations.

When I started building the product wizard for CIVA, generating this matrix seemed like simple math: take the array of selected options and compute the Cartesian product.

Then I tested it with real catalog data and hit a massive usability wall.


The Naive Array Trap

Here is how matrix generators are typically written in frontend tutorials:

// The naive approach: re-computes and wipes everything
const generateMatrix = (options) => {
  return cartesianProduct(options).map((combo) => ({
    sku: autoGenerateSku(combo),
    price: 0,
    stock: 0,
    attributes: combo,
  }));
};

Suppose a store manager spends twenty minutes filling out custom wholesale prices and specific warehouse SKUs for eight laptop combinations.

Then they realize they forgot to check the "1TB SSD" box.

They click back, check "1TB", and return to the matrix table. With the naive implementation above, the generator re-runs, wipes the entire array, and resets all eight previously filled rows back to $0.00 and empty stock.

All twenty minutes of manual data entry: gone.


The Realization: Treat Variants Like Virtual DOM Nodes

React solves this exact problem with the Virtual DOM: when a parent list updates, React doesn't destroy all DOM nodes; it matches elements by a stable key and only updates what actually changed.

I applied the same reconciliation pattern to our variant generator:

  1. Generate a Deterministic Key for Every Combination: Sort attribute IDs so Color:Black | RAM:16GB always produces the exact same hash regardless of array ordering.
const getVariantKey = (attributes) =>
  attributes
    .map((attr) => `${attr.attributeId}:${attr.optionId}`)
    .sort()
    .join('|');
  1. Reconcile Existing State Against the New Matrix: When options change, compute the new combinations, look up any existing variant by its deterministic key, and preserve all custom user inputs (prices, images, SKUs, inventory thresholds).
const reconciled = nextCombos.map((combo) => {
  const key = getVariantKey(combo);
  const existing = currentVariantsMap.get(key);

  // If this combination already existed, keep the user's data!
  if (existing) {
    return { ...existing, attributes: combo };
  }

  // Otherwise, initialize a fresh row with smart defaults
  return createDefaultVariant(combo);
});

The Takeaway

Good algorithms don't just calculate mathematically correct outputs. They respect human effort.

Whenever you build a generative UI tool—whether it's a pricing matrix, a dynamic form generator, or a schedule builder—always separate the mathematical computation from user-entered state.

— Abu Bakar Hasan

If this resonated, let's talk. Get in touch →