ts-07-mathjs-evaluate
1.000
Challenge · difficulty 4/5
# Compile and evaluate expressions (mathjs)
Implement **`solution.ts`** using **mathjs** (already installed — import what you need,
e.g. `import { compile } from "mathjs";`).
Export exactly one function:
```ts
export function evaluateAll(expr: string, scopes: Record<string, number>[]): number[];
```
Behavior:
- **Compile the expression once** with mathjs (`compile(expr)`), then evaluate the
compiled expression against **each** scope in `scopes`, in order.
- Return an array of the numeric results (one per scope). Convert each result to a
`number` so the return type is exactly `number[]`.
- An empty `scopes` array returns `[]`.
The expression may reference variables supplied by the scope and may use mathjs built-in
functions (e.g. `sqrt`, `max`, `sin`).
Examples:
```ts
evaluateAll("a^2 + b", [{ a: 3, b: 1 }, { a: 2, b: 5 }]);
// => [10, 9]
evaluateAll("sqrt(x) + max(y, 1)", [{ x: 9, y: 4 }]);
// => [7] // sqrt(9)=3, max(4,1)=4
evaluateAll("a + b", []);
// => []
```
Keep it fully typed (must pass `tsc --noEmit` in strict mode). Do not use `any` in the
public API.
tests/solution.test.ts
import { test } from "node:test";
import { strict as assert } from "node:assert";
import { evaluateAll } from "./solution.ts";
test("evaluates with multiple variables across scopes", () => {
assert.deepEqual(evaluateAll("a^2 + b", [{ a: 3, b: 1 }, { a: 2, b: 5 }]), [10, 9]);
});
test("preserves scope order", () => {
assert.deepEqual(evaluateAll("x * 10", [{ x: 1 }, { x: 2 }, { x: 3 }]), [10, 20, 30]);
});
test("uses built-in functions", () => {
assert.deepEqual(evaluateAll("sqrt(x) + max(y, 1)", [{ x: 9, y: 4 }]), [7]);
});
test("empty scopes returns empty array", () => {
assert.deepEqual(evaluateAll("a + b", []), []);
});
test("single scope", () => {
assert.deepEqual(evaluateAll("2 * a + 1", [{ a: 20 }]), [41]);
});
test("result is a number array", () => {
const out = evaluateAll("a / b", [{ a: 6, b: 2 }, { a: 9, b: 3 }]);
assert.equal(typeof out[0], "number");
assert.deepEqual(out, [3, 3]);
});
test("compiles once and reuses for differing variable values", () => {
const out = evaluateAll("a + b + c", [
{ a: 1, b: 2, c: 3 },
{ a: 10, b: 20, c: 30 },
]);
assert.deepEqual(out, [6, 60]);
});
System prompt
You are an expert programmer. Solve the task exactly as specified. Output your solution as fenced code blocks using the required file name(s) and the exact function/type signatures requested. Prefer correctness; do not include prose outside code unless asked.Proposed solution
```ts
import { compile } from "mathjs";
export function evaluateAll(expr: string, scopes: Record<string, number>[]): number[] {
const compiled = compile(expr);
return scopes.map(scope => Number(compiled.evaluate(scope)));
}
```Test output (stdout)
TAP version 13
# Subtest: evaluates with multiple variables across scopes
ok 1 - evaluates with multiple variables across scopes
---
duration_ms: 1.628723
type: 'test'
...
# Subtest: preserves scope order
ok 2 - preserves scope order
---
duration_ms: 0.208458
type: 'test'
...
# Subtest: uses built-in functions
ok 3 - uses built-in functions
---
duration_ms: 0.478145
type: 'test'
...
# Subtest: empty scopes returns empty array
ok 4 - empty scopes returns empty array
---
duration_ms: 0.089249
type: 'test'
...
# Subtest: single scope
ok 5 - single scope
---
duration_ms: 0.103579
type: 'test'
...
# Subtest: result is a number array
ok 6 - result is a number array
---
duration_ms: 0.120279
type: 'test'
...
# Subtest: compiles once and reuses for differing variable values
ok 7 - compiles once and reuses for differing variable values
---
duration_ms: 0.128189
type: 'test'
...
1..7
# tests 7
# suites 0
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 321.12667
Reasoning (chain-of-thought)
The user wants a TypeScript function `evaluateAll` that uses the `mathjs` library.
The function takes an expression string and an array of scopes (objects mapping variable names to numbers).
It should compile the expression once, then evaluate it for each scope.
It should return an array of numbers.
Let's check the mathjs API.
`import { compile } from 'mathjs';`
`const node = compile(expr);`
`node.evaluate(scope)` returns a mathjs type (could be `number`, `Unit`, `Matrix`, etc.).
The problem says "Convert each result to a `number` so the return type is exactly `number[]`."
Mathjs has a `.toNumber()` method on its types, or we can just cast it if we know it's a number, but to be safe, we can use `Number(result)` or `result.toNumber()` if available, or just cast to `number` since the problem implies the result is numeric. Actually, mathjs's `evaluate` returns `any` or `mathjs.MathNode`? Wait, `compile` returns a `MathNode` or `CompiledExpression`. In mathjs v11/v12, `compile` returns a `CompiledExpression` which has an `evaluate` method.
Let's check mathjs types: `import { compile, CompiledExpression } from 'mathjs';`
Actually, `compile` returns `CompiledExpression`.
`node.evaluate(scope)` returns `any` in older versions, but in newer versions it might be typed. To be safe, I'll cast to `number` or use `Number()`. The problem says "Convert each result to a `number`", so `Number(result)` or `result as number` is fine. I'll use `Number(result)` to be explicit, or just cast since mathjs usually returns JS numbers for numeric expressions. Actually, `result.toNumber()` is safer if it's a mathjs type, but `Number(result)` works for plain numbers. I'll stick to `Number(result)` or just `result as number`. Wait, mathjs `evaluate` can return a `Unit` or `Matrix`. The problem says "Convert each result to a `number`", so `Number(result)` might fail for Units. But usually, for simple expressions, it returns a number. I'll use `Number(result)` or `(result as any).toNumber?.() ?? Number(result)`. Actually, mathjs has `import { number } from 'mathjs'`? No.
Let's just use `Number(result)` or cast to `number`. The examples show simple numbers. I'll cast to `number` or use `Number()`. To be strictly compliant with "Convert each result to a `number`", I'll use `Number(result)`.
Wait, `mathjs`'s `compile` function:
```ts
import { compile } from 'mathjs';
const expr = compile(exprStr);
const res = expr.evaluate(scope);
```
In mathjs, `evaluate` returns `any`. I'll just cast to `number`. Or use `Number(res)`.
Let's write it out:
```ts
import { compile } from 'mathjs';
export function evaluateAll(expr: string, scopes: Record<string, number>[]): number[] {
const compiled = compile(expr);
return scopes.map(scope => Number(compiled.evaluate(scope)));
}
```
Wait, `mathjs` types might complain if `scope` is `Record<string, number>`. `evaluate` expects `Record<string, any>` or similar. That's fine.
Let's verify mathjs version compatibility. `compile` is exported from `mathjs`.
In mathjs v11+, `compile` returns `CompiledExpression`.
`evaluate` method signature: `evaluate(scope?: Record<string, any>): any`
So `compiled.evaluate(scope)` is fine.
One detail: `Number()` might convert `Unit` to `NaN` or throw? Actually, `Number(unit)` works in JS if unit has `valueOf`, but mathjs Units don't necessarily. However, the problem says "Convert each result to a `number`", implying the result is numeric. I'll just cast to `number` or use `Number()`. I'll stick with `Number(compiled.evaluate(scope))` or `(compiled.evaluate(scope) as number)`. To be safe against mathjs returning a `BigNumber` or `Unit`, I could use `compiled.evaluate(scope).toNumber?.() ?? Number(compiled.evaluate(scope))`, but that's overcomplicating. The problem says "Convert each result to a `number`", so `Number(result)` is probably expected, or just casting. I'll use `Number(result)`.
Actually, mathjs has a `number` function? No.
I'll just use `Number(compiled.evaluate(scope))`.
Wait, `mathjs`'s `evaluate` can return a `number` directly. I'll just cast to `number` to satisfy TypeScript strict mode without `any` in public API, but internally it's fine.
The problem says "Do not use `any` in the public API." Internal usage is fine.
Let's refine:
```ts
import { compile } from 'mathjs';
export function evaluateAll(expr: string, scopes: Record<string, number>[]): number[] {
const compiled = compile(expr);
return scopes.map(scope => Number(compiled.evaluate(scope)));
}
```
Is there any issue with `Number()` on mathjs results? If it returns a plain JS number, `Number()` works. If it returns a `BigNumber`, `Number()` works. If it returns a `Unit`, `Number()` might not work well, but the problem implies numeric results. I'll stick with `Number()`. Alternatively, `(compiled.evaluate(scope) as number)` is simpler. I'll use `Number()` to explicitly "convert" as requested.
Wait, `mathjs`'s `compile` might be `import { compile } from 'mathjs'`. Yes.
Let's check if `mathjs` exports `compile` directly. Yes.
One thing: `mathjs` types for `compile` might require `import { compile, CompiledExpression } from 'mathjs'`. Not necessary.
I will write the solution exactly as requested.
```ts
import { compile } from 'mathjs';
export function evaluateAll(expr: string, scopes: Record<string, number>[]): number[] {
const node = compile(expr);
return scopes.map(scope => Number(node.evaluate(scope)));
}
```
Wait, `node.evaluate` might need `scope` to be passed correctly. `Record<string, number>` is fine.
The problem says "Convert each result to a `number` so the return type is exactly `number[]`."
Using `Number()` is safe.
Let's double check the exact signature:
`export function evaluateAll(expr: string, scopes: Record<string, number>[]): number[];`
One minor point: `mathjs`'s `evaluate` can take an optional scope. If `scope` is empty object, it works.
The code looks solid.
I'll produce the final answer.
No extra fluff.
Check imports: `import { compile } from "mathjs";`
Check types: `Record<string, number>[]` matches.
Check behavior: compiles once, evaluates per scope, returns number array.
Empty scope
…[793 chars elided]…
turn scopes.map(scope => Number(compiled.evaluate(scope)));
}
```
This matches all requirements.
I will output this.
One detail: `mathjs` might be imported as `import { compile } from 'mathjs';` (single or double quotes, doesn't matter).
The prompt says `import { compile } from "mathjs";`.
I'll use that.
Done.
Let's verify strict mode compatibility: `Record<string, number>` is fine. `Number()` is fine. `compile` is fine. `evaluate` is fine. No `any` in public API.
All good.
Output matches request.✅
Proceed.
Self-Correction/Verification during thought:
Could `compiled.evaluate(scope)` return something that `Number()` doesn't handle well? In mathjs, numeric results are usually JS numbers or BigNumbers. `Number()` handles both. If it's a Unit, `Number()` might return NaN, but the problem implies valid numeric results. I'll stick with `Number()`. Alternatively, I can just cast to `number` since the problem says "Convert each result to a `number`", which might just mean ensuring the return type is `number[]`. I'll use `Number()` to be explicit about conversion.
Actually, `mathjs` has a `number` function? No.
I'll keep it as is.
Ready.
Output matches.
Proceeds.
[Output Generation] -> code block.
All steps verified.
Final Answer Generation.
```typescript
import { compile } from "mathjs";
export function evaluateAll(expr: string, scopes: Record<string, number>[]): number[] {
const compiled = compile(expr);
return scopes.map(scope => Number(compiled.evaluate(scope)));
}
```
Looks correct.✅
Note: `mathjs`'s `evaluate` method signature in recent versions might be `evaluate(scope?: Record<string, any>): any`. Passing `Record<string, number>` is compatible.
The solution is minimal and correct.
Done.
Output matches request.
Proceeds.
[Final Check]
- Compiles once? Yes.
- Evaluates against each scope? Yes.
- Returns number[]? Yes.
- Empty scopes returns []? Yes.
- Fully typed, strict mode compatible? Yes.
- No `any` in public API? Yes.
All constraints met.
Output matches.
Done.
[Output Generation] (proceeds)