Skip to content

Commit

Permalink
Implement Fraction JSON serialization and deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
frostburn committed May 4, 2024
1 parent e157c2f commit 4d83be8
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/__tests__/fraction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,3 +725,43 @@ describe('Fraction', () => {
);
});
});

describe('JSON serialization', () => {
it('can serialize an array of fractions along with other data', () => {
const serialized = JSON.stringify([
new Fraction(42),
2,
new Fraction(-5, 3),
new Fraction('1.234'),
'hello',
new Fraction({s: 0, n: 0, d: 1}),
]);

expect(serialized).toBe(
'[{"n":42,"d":1},2,{"n":-5,"d":3},{"n":617,"d":500},"hello",{"n":0,"d":1}]'
);
});

it('can revive an array of fractions along with other data', () => {
const serialized =
'[{"n":42,"d":1},2,{"n":-5,"d":3},{"n":617,"d":500},"hello",{"n":0,"d":1}]';
const data = JSON.parse(serialized, Fraction.reviver);
expect(data).toHaveLength(6);

expect(data[0]).toBeInstanceOf(Fraction);
expect(data[0]).toEqual({s: 1, n: 42, d: 1});

expect(data[1]).toBe(2);

expect(data[2]).toBeInstanceOf(Fraction);
expect(data[2]).toEqual({s: -1, n: 5, d: 3});

expect(data[3]).toBeInstanceOf(Fraction);
expect(data[3].equals('1.234')).toBe(true);

expect(data[4]).toBe('hello');

expect(data[5]).toBeInstanceOf(Fraction);
expect(data[5]).toEqual({s: 0, n: 0, d: 1});
});
});
38 changes: 38 additions & 0 deletions src/fraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,44 @@ export class Fraction {
return result + decimals + '...';
}

/**
* Serialize the fraction to a JSON compatible object.
* @returns An object with properties 'n', and 'd' corresponding to a signed numerator and an unsigned denominator respectively.
*/
toJSON(): UnsignedFraction {
return {
n: this.n * this.s,
d: this.d,
};
}

/**
* Revive a {@link Fraction} instance produced by Fraction.toJSON(). Return everything else as is.
*
* Intended usage:
* ```ts
* const data = JSON.parse(serializedData, Fraction.reviver);
* ```
*
* @param key Property name.
* @param value Property value.
* @returns Deserialized {@link Fraction} instance or other data without modifications.
* @throws An error if the numerator or denominator exceeds `Number.MAX_SAFE_INTEGER`.
*/
static reviver(key: string, value: any) {
if (
typeof value === 'object' &&
'n' in value &&
Number.isInteger(value.n) &&
'd' in value &&
Number.isInteger(value.d) &&
Object.keys(value).length === 2
) {
return new Fraction(value as UnsignedFraction);
}
return value;
}

/**
* Returns an array of continued fraction elements.
*
Expand Down

0 comments on commit 4d83be8

Please sign in to comment.