#7160·reflex

Deep equality Var operation (lodash `isEqual` equivalent)

Author: masenfCreated Sep 15, 2026Updated Sep 15, 2026
Labelsfeature request

Describe the Features

Requested on Discord by Bert. There is currently no Var operation for structural (deep) equality. Var.__eq__ compiles to a JS identity comparison, so two structurally identical dicts never compare equal on the frontend:

python
a = rx.Var.create({"a": 1, "b": 2})
b = rx.Var.create({"b": 2, "a": 1})

str(a == b)
# (({ ["a"] : 1, ["b"] : 2 })?.valueOf?.() === ({ ["b"] : 2, ["a"] : 1 })?.valueOf?.())
# -> false at runtime: `===` on objects is reference equality

The obvious workaround — comparing to_string(), i.e. JSON.stringify — is key-insertion-order dependent, so it is also false here:

python
str(a.to_string() == b.to_string())
# ((JSON.stringify(({ ["a"] : 1, ["b"] : 2 })))?.valueOf?.() === (JSON.stringify(({ ["b"] : 2, ["a"] : 1 })))?.valueOf?.())
# JSON.stringify({a:1,b:2}) === '{"a":1,"b":2}'
# JSON.stringify({b:2,a:1}) === '{"b":2,"a":1}'   -> false

The request is for a Var operation equivalent to lodash's isEqual: deep structural comparison of objects/arrays/primitives, returning a BooleanVar usable anywhere on the frontend.

  • Purpose: compare two state-derived structures on the frontend, without round-tripping to the backend and without hand-rolling a comparison per data shape.

  • Use case (from the reporter): a form that highlights each field that differs from what was previously submitted. Most fields are scalars, so previous == current is enough. One field is a PaintScheme — a mapping of house element to the color chosen for it, picked from a catalog. Changing the whole scheme is a cheap id/name comparison, but changing individual element colors within a scheme means comparing the entire nested mapping against the previously submitted one. The form is data-driven — the same code renders many different form configurations and item types, of which PaintScheme is one — so a hard-coded per-shape comparison isn't available.

Current workaround

Since reduce landed (#6701), this can be built by hand: iterate the keys, & together the per-key comparisons.

python
S.prev.keys().reduce(
    lambda acc, k: acc & (S.prev[k] == S.curr[k]),
    True,
) & (S.prev.length() == S.curr.length())

The reporter notes this took about half an hour to work out and explicitly is not claiming there's no alternative — so this is an ergonomics request, not a blocker. The rough edges of the workaround:

  • It handles exactly one level. Each additional level of nesting needs another hand-written pass — in the reporter's case the values were ColorValue objects, so the real comparison sits one level below the key.
  • The explicit length() check is needed to catch keys present in only one of the two dicts; it's easy to leave off and get a false positive.
  • It has to be written differently again for arrays.

Additional context

  • Related: #7084 deep-compares memoized prop objects with .to_string() (JSON.stringify), which carries the same key-order caveat shown above. A shared deep-equality primitive could back both.
  • Naming: Var.equals() is already taken by a compile-time method that compares _js_expr / _var_type / _var_data (i.e. "are these the same Var?"), not runtime values. A new runtime operation should not reuse that name.
  • Implementation: likely a var_operation emitting a small recursive comparison helper in the JS runtime. Pulling in lodash for one function seems unwarranted; there are small single-purpose deep-equal packages if a dependency is preferred over a hand-written helper.