@mridul_.bansal/marks-kit
v1.0.0
Published
Calculate marks percentages and subject-wise pass/fail summaries.
Maintainers
Readme
marks-kit
A tiny, dependency-free JavaScript library for student marks. It does two things: turn marks into a percentage, and summarize several subjects into a combined percentage with subject-wise pass/fail results.
The 40% passing threshold is a configurable demonstration default. This is not an official school or university result policy, and the package contains no letter grades, CGPA conversions, or institution-specific rules.
Install
npm install @mridul_.bansal/marks-kitRequires Node.js 22 or newer. There are no runtime dependencies.
Usage
CommonJS
const { calculatePercentage, summarizeMarks } = require('@mridul_.bansal/marks-kit');
console.log(calculatePercentage(41, 50)); // 82
console.log(summarizeMarks([
{ name: 'Maths', obtained: 78, total: 100 },
{ name: 'DSA', obtained: 84, total: 100 },
{ name: 'Networks', obtained: 36, total: 50 }
]).percentage); // 79.2ESM
This package is CommonJS, so import the default export and destructure from it. Named ESM imports are not part of the supported API.
import marksKit from '@mridul_.bansal/marks-kit';
const { calculatePercentage } = marksKit;
console.log(calculatePercentage(41, 50)); // 82API
calculatePercentage(obtained, total)
Returns (obtained / total) * 100 as a number, rounded to two decimal places.
calculatePercentage(85, 100); // 85
calculatePercentage(41, 50); // 82
calculatePercentage(1, 3); // 33.33
calculatePercentage(0, 100); // 0Both arguments must be finite numbers, with total > 0, obtained >= 0, and
obtained <= total. Finite decimal marks are allowed.
summarizeMarks(subjects, options?)
subjects is a nonempty array of { name, obtained, total } objects.
options.passingPercentage is a finite number from 0 to 100 and defaults to
40 when the options object or the property is omitted.
summarizeMarks(
[
{ name: 'Maths', obtained: 78, total: 100 },
{ name: 'DSA', obtained: 84, total: 100 },
{ name: 'Networks', obtained: 36, total: 50 }
],
{ passingPercentage: 40 }
);returns
{
totalObtained: 198,
totalMaximum: 250,
percentage: 79.2,
passingPercentage: 40,
passed: true,
passedSubjects: 3,
failedSubjects: 0,
subjects: [
{ name: 'Maths', obtained: 78, total: 100, percentage: 78, passed: true },
{ name: 'DSA', obtained: 84, total: 100, percentage: 84, passed: true },
{ name: 'Networks', obtained: 36, total: 50, percentage: 72, passed: true }
]
}Subject names are trimmed in the output. Input order is preserved, duplicate names are kept as separate entries, and the inputs are never modified: every returned object is new.
How the numbers work
The combined percentage is weighted, not averaged. It is total marks obtained divided by total maximum marks:
summarizeMarks([
{ name: 'Lab', obtained: 45, total: 50 },
{ name: 'Theory', obtained: 50, total: 100 }
]).percentage; // 63.33, i.e. 95/150 — not the 70 you get by averaging 90 and 50Every subject must pass. A strong subject cannot compensate for a failed
one, so passed is true only when every subject meets the threshold:
summarizeMarks([
{ name: 'Maths', obtained: 100, total: 100 },
{ name: 'DSA', obtained: 20, total: 100 }
]); // percentage: 60, but passed: false at the default 40% thresholdRounding is for display only. Pass/fail is decided on the unrounded percentage, so a reported value can look like a pass and still fail:
summarizeMarks([{ name: 'Maths', obtained: 39.999, total: 100 }]);
// subjects[0].percentage is 40, but subjects[0].passed is false at a 40% thresholdThese are ordinary JavaScript floating-point calculations, not arbitrary-precision decimal arithmetic.
Validation errors
Inputs are validated before anything is calculated. Numeric strings are rejected rather than coerced.
| Condition | Error |
| --- | --- |
| A mark, total, or threshold is not of type number (including numeric strings, null, booleans, BigInt) | TypeError |
| A mark, total, or threshold is NaN or infinite | RangeError |
| total <= 0, obtained < 0, or obtained > total | RangeError |
| subjects is not an array, or an entry is not a plain object | TypeError |
| subjects is empty | RangeError |
| A subject name is not a string | TypeError |
| A subject name is blank after trimming | RangeError |
| options is provided but is not a plain object | TypeError |
| passingPercentage is outside 0 to 100 | RangeError |
| Summing the marks overflows to a nonfinite total | RangeError |
Local development
npm test
npm run demoTests use Node's built-in test runner; there is no build step and nothing to install.
License
MIT
