Skip to content

Commit dc9ec79

Browse files
committed
add EncodeOptions#ignoreUndefined to ignore undefined values in maps
1 parent c98fd4e commit dc9ec79

File tree

4 files changed

+47
-5
lines changed

4 files changed

+47
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ maxDepth | number | `100`
116116
initialBufferSize | number | `2048`
117117
sortKeys | boolean | false
118118
forceFloat32 | boolean | false
119+
ignoreUndefined | boolean | false
119120
context | user-defined | -
120121

121122
### `decode(buffer: ArrayLike<number> | ArrayBuffer, options?: DecodeOptions): unknown`

src/Encoder.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class Encoder<ContextType> {
1919
readonly initialBufferSize = DEFAULT_INITIAL_BUFFER_SIZE,
2020
readonly sortKeys = false,
2121
readonly forceFloat32 = false,
22+
readonly ignoreUndefined = false,
2223
) {}
2324

2425
encode(object: unknown, depth: number): void {
@@ -231,12 +232,26 @@ export class Encoder<ContextType> {
231232
}
232233
}
233234

235+
countWithoutUndefined(object: Record<string, unknown>, keys: ReadonlyArray<string>): number {
236+
let count = 0;
237+
238+
for (const key of keys) {
239+
if (object[key] !== undefined) {
240+
count++;
241+
}
242+
}
243+
244+
return count;
245+
}
246+
234247
encodeMap(object: Record<string, unknown>, depth: number) {
235248
const keys = Object.keys(object);
236249
if (this.sortKeys) {
237250
keys.sort();
238251
}
239-
const size = keys.length;
252+
253+
const size = this.ignoreUndefined ? this.countWithoutUndefined(object, keys) : keys.length;
254+
240255
if (size < 16) {
241256
// fixmap
242257
this.writeU8(0x80 + size);
@@ -252,10 +267,13 @@ export class Encoder<ContextType> {
252267
throw new Error(`Too large map object: ${size}`);
253268
}
254269

255-
for (let i = 0; i < size; i++) {
256-
const key = keys[i];
257-
this.encodeString(key);
258-
this.encode(object[key], depth + 1);
270+
for (const key of keys) {
271+
const value = object[key];
272+
273+
if (!(this.ignoreUndefined && value === undefined)) {
274+
this.encodeString(key);
275+
this.encode(value, depth + 1);
276+
}
259277
}
260278
}
261279

src/encode.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ export type EncodeOptions<ContextType = undefined> = Partial<
1515
* Only use it if precisions don't matter.
1616
*/
1717
forceFloat32: boolean;
18+
19+
/**
20+
* If `true`, an object property with `undefined` value are ignored.
21+
* e.g. `{ foo: undefined }` will be encoded as `{}`, as `JSON.stringify()` does.
22+
*
23+
* The default is `false`. Note that it needs more time to encode.
24+
*/
25+
ignoreUndefined: boolean;
1826
}>
1927
> &
2028
ContextOf<ContextType>;
@@ -38,6 +46,7 @@ export function encode<ContextType>(
3846
options.initialBufferSize,
3947
options.sortKeys,
4048
options.forceFloat32,
49+
options.ignoreUndefined,
4150
);
4251
encoder.encode(value, 1);
4352
return encoder.getUint8Array();

test/encode.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@ describe("encode", () => {
2525
});
2626
});
2727

28+
context("ignoreUndefined", () => {
29+
it("encodes { foo: undefined } as is by default", () => {
30+
assert.deepStrictEqual(decode(encode({ foo: undefined })), { foo: null });
31+
});
32+
33+
it("encodes { foo: undefined } as is with `ignoreUndefined: false`", () => {
34+
assert.deepStrictEqual(decode(encode({ foo: undefined }, { ignoreUndefined: false })), { foo: null });
35+
});
36+
37+
it("encodes { foo: undefined } to {} with `ignoreUndefined: true`", () => {
38+
assert.deepStrictEqual(decode(encode({ foo: undefined }, { ignoreUndefined: true })), {});
39+
});
40+
});
41+
2842
context("ArrayBuffer as buffer", () => {
2943
const buffer = encode([1, 2, 3]);
3044
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteLength);

0 commit comments

Comments
 (0)