Skip to content

fix(vars): compare array vars element-wise with Python semantics - #7656

Open
anishgurjar wants to merge 1 commit into
reflex-dev:mainfrom
anishgurjar:fix-array-var-comparison
Open

anishgurjar wants to merge 1 commit into
reflex-dev:mainfrom
anishgurjar:fix-array-var-comparison

Conversation

@anishgurjar

Copy link
Copy Markdown

Fixes #7654

Problem

Array ordering comparisons (<, <=, >, >=) on array vars compile to a direct JS comparison between the two arrays. JS does not compare arrays element-wise; it coerces both sides to comma-joined strings, so [1, 10, 0] < [1, 9, 0] evaluates "1,10,0" < "1,9,0" and returns true, where Python compares element-wise and returns False.

Fix

The four array ordering operations now compile through a new compareArrays frontend helper ($/utils/helpers/array.js, same pattern as compareDatetime) that mirrors Python list comparison:

  • pairwise element comparison; the first unequal pair decides the result
  • when every shared element is equal, the shorter array sorts first
  • nested arrays compare recursively
  • booleans compare as numbers, matching Python where bool subclasses int ([True] == [1], [True] < [2])
  • a NaN operand pair yields NaN, matching Python where every ordering comparison against float("nan") is false
  • an incomparable element pair (mismatched or unsupported types, where Python raises TypeError) also yields NaN, so every ordering result is false instead of a silent string comparison

Equality (== / !=) is unchanged and keeps its existing comparison path.

Scope and remaining divergences

This covers scalar (number/string/bool) and nested-array elements, which is what serialized array vars contain in practice. Remaining known divergences from Python, unchanged by this PR:

  • string comparison is by UTF-16 code units, so astral-plane characters can order differently than Python's code-point ordering
  • elements serialized to strings before comparison (e.g. dates inside arrays) compare as their serialized form, not their original type
  • plain-object elements are treated as incomparable (ordering is false); Python compares dicts for equality and raises on ordering

Tests

  • tests/units/test_var.py: JS emission for all four operators plus the compareArrays import wiring
  • tests/units/vars/array_helpers.test.mjs: runtime suites against the real template module (numeric ordering, prefix rule, strings/booleans, nested arrays, NaN operands, bool/number mixing, incomparable types)
  • tests/units/vars/test_array_helpers_template.py: pytest wrapper that executes the runtime suites (skips when Node.js is unavailable)
  • Verified in a compiled app in a real browser: the unpatched build renders the string-coerced result, the patched build renders the Python-consistent result

Test limits: ruff check, ruff format, codespell, biome, pyright on the touched files, the touched pytest modules, and node --test were run directly and pass; the uv-managed pre-commit env hooks and full CI have not run on this branch.

@masenf - following the compareDatetime precedent here; would appreciate a look when you have a moment.

Array comparison operations (<, <=, >, >=) compiled to direct JS array
comparison, which coerces arrays to comma-joined strings, so [1, 10, 0]
< [1, 9, 0] evaluated true while Python compares element-wise and
returns false.

Compile the four ordering operations through a new compareArrays
frontend helper that mirrors Python list semantics: pairwise element
comparison, shorter-prefix ordering, recursive nested arrays, booleans
as numbers (bool subclasses int), and NaN for NaN operands or
incomparable element types so every ordering result is false.

Refs reflex-dev#7654
@anishgurjar
anishgurjar requested a review from a team as a code owner October 26, 2026 01:25

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/reflex-base/src/reflex_base/.templates/web/utils/helpers/array.js">

<violation number="1" location="packages/reflex-base/src/reflex_base/.templates/web/utils/helpers/array.js:35">
P2: This numeric path does not preserve Python’s arbitrary-precision integer ordering: values above `Number.MAX_SAFE_INTEGER` have already collapsed to the same JavaScript `Number` before `compareArrays` compares them. Preserve integer precision in the frontend representation or explicitly reject/document this unsupported range so the helper does not claim Python list semantics for it.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

const rightIsNumeric =
typeof right === "number" || typeof right === "boolean";
if (leftIsNumeric && rightIsNumeric) {
const leftNumber = Number(left);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This numeric path does not preserve Python’s arbitrary-precision integer ordering: values above Number.MAX_SAFE_INTEGER have already collapsed to the same JavaScript Number before compareArrays compares them. Preserve integer precision in the frontend representation or explicitly reject/document this unsupported range so the helper does not claim Python list semantics for it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/reflex-base/src/reflex_base/.templates/web/utils/helpers/array.js, line 35:

<comment>This numeric path does not preserve Python’s arbitrary-precision integer ordering: values above `Number.MAX_SAFE_INTEGER` have already collapsed to the same JavaScript `Number` before `compareArrays` compares them. Preserve integer precision in the frontend representation or explicitly reject/document this unsupported range so the helper does not claim Python list semantics for it.</comment>

<file context>
@@ -0,0 +1,51 @@
+    const rightIsNumeric =
+      typeof right === "number" || typeof right === "boolean";
+    if (leftIsNumeric && rightIsNumeric) {
+      const leftNumber = Number(left);
+      const rightNumber = Number(right);
+      if (Number.isNaN(leftNumber) || Number.isNaN(rightNumber)) {
</file context>

@greptile-apps

greptile-apps Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The change affects browser comparisons for Reflex array-ordering expressions.

The comparison change appears sound within its stated scope, but the repository’s release-note and test requirements must be satisfied before merging.

Findings

  1. P2 Release notes omit this fix ▶
  2. P2 Tests miss the package layout ▶
  3. P2 Tests omit required docstrings ▶

Summary

Array ordering now calls compareArrays instead of comparing JavaScript arrays directly.

  • Array ordering now compares values one by one like Python lists.

Intentional limits supplied by anishgurjar: UTF-16 string ordering, dates compared in their serialized form, and plain objects treated as incomparable. Equality remains unchanged.

Reviews (1) · Last reviewed commit: "fix(vars): compare array vars element-wi..." · Reviewed by Greptile

Comment on lines +1831 to +1834
return var_operation_return(
js_expression=f"(compareArrays({lhs!s}, {rhs!s}) < 0)",
var_type=bool,
var_data=VarData(imports=_ARRAY_COMPARE_IMPORT),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Release notes omit this fix

This user-facing array ordering change has no bugfix fragment under packages/reflex-base/news/. CLAUDE.md requires a fragment for each affected package. Add a short entry explaining that arrays now compare element by element instead of as comma-joined strings. This repository requirement must be satisfied before merging.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread tests/units/test_var.py
(op.ge, ">="),
],
)
def test_array_comparison_uses_compare_arrays(operation, operator):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tests miss the package layout

The new array comparison tests cover reflex_base.vars.sequence but sit in the top-level test_var.py. CLAUDE.md requires subpackage unit tests to include the corresponding package path. Move these tests under tests/units/reflex_base/vars/ before merging to satisfy that requirement.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread tests/units/test_var.py
(op.ge, ">="),
],
)
def test_array_comparison_uses_compare_arrays(operation, operator):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tests omit required docstrings

Neither test_array_comparison_uses_compare_arrays nor test_array_comparison_imports_compare_arrays has a docstring. CLAUDE.md requires Google-style docstrings on all functions. Add a one-line summary to each test before merging to satisfy that requirement.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Comparing list Vars with < > <= >= gives wrong results (arrays are compared as strings in JS)

1 participant