Lập trình · 18/10/2026

JavaScript Map: Avoid Confusing Number, String and Object Keys

An API returns a string ID, but the interface looks it up as a number and finds nothing. Map key types and identity are part of the lookup contract. Switching from an object to Map does not automatically repair inconsistent IDs.

JavaScript Map: tránh nhầm khóa số, khóa chuỗi và object

An API returns a string ID, but the interface looks it up as a number and finds nothing. Map key types and identity are part of the lookup contract. Switching from an object to Map does not automatically repair inconsistent IDs.

These examples separate key selection, presence checks and duplicate handling. They were checked locally with Node.js without calling APIs or changing production data.

1. Number 1 and string '1' are different keys

const lookup = new Map();
lookup.set(1, 'numeric');
lookup.set('1', 'text');

console.log(lookup.size);     // 2
console.log(lookup.get(1));   // numeric
console.log(lookup.get('1')); // text

MDN Map documents key types and comparison. Establish a common ID type at the data boundary instead of scattering conversions across screens.

If '001' and '1' are distinct business codes, converting both to Number loses meaning. For string IDs, decide whether empty strings or surrounding whitespace are acceptable before indexing.

2. Object keys use reference identity

const key = { id: 1 };
const cache = new Map([[key, 'ready']]);

console.log(cache.get(key));       // ready
console.log(cache.get({ id: 1 })); // undefined

Two objects with the same id field are not the same object. Entity lookup usually needs a canonical business ID rather than a newly constructed object per call.

The ECMAScript Map specification defines entry and key behavior. When deliberately caching by instance, review lifetime and removal. This example implements neither eviction nor a capacity limit.

3. get() does not replace has()

const states = new Map([['task-1', undefined]]);

console.log(states.get('task-1')); // undefined
console.log(states.get('task-2')); // undefined
console.log(states.has('task-1')); // true
console.log(states.has('task-2')); // false

When undefined is a valid stored value, get() cannot distinguish it from a missing key. Use Map.prototype.has() for presence.

A truthiness check on get() also discards false, zero and an empty string. Model states such as not loaded, loading, not found and available explicitly rather than overloading a falsy value.

4. Decide duplicate policy before indexing records

This reader accepts an array of ordinary records with nonempty string IDs and no surrounding whitespace. It rejects duplicates and retains references to the original records:

function indexRecords(rows) {
  if (!Array.isArray(rows)) throw new TypeError('Expected an array');
  const index = new Map();

  for (const row of rows) {
    if (row === null || typeof row !== 'object' || Array.isArray(row)
        || !Object.hasOwn(row, 'id')
        || typeof row.id !== 'string'
        || row.id.length === 0 || row.id.trim() !== row.id) {
      throw new TypeError('Expected a record with a canonical string id');
    }
    if (index.has(row.id)) throw new Error('Duplicate id');
    index.set(row.id, row);
  }
  return index;
}

Rejecting duplicates is a contract choice. Other products might retain the first record, retain the last or merge records. Make that explicit rather than accidentally hiding data behind the latest set().

This is not a complete schema validator or authorization check. Its scope is ordinary data objects, not untrusted getters or proxies. The backend still enforces its own contract.

5. Choose a JSON format deliberately

const labels = new Map([['a', 'Alpha'], ['b', 'Beta']]);

console.log(JSON.stringify(labels)); // {}
const json = JSON.stringify([...labels]);
const restored = new Map(JSON.parse(json));
console.log(restored.get('a')); // Alpha

Ordinary Map entries are not directly serialized by JSON.stringify. See MDN JSON.stringify(). This example explicitly chooses an array of key/value pairs.

The round-trip uses string keys and values only. It does not prove arbitrary maps survive serialization: undefined, object identity and non-JSON types need separate policies. Validate pair structure, key types and duplicates when receiving external data before constructing a Map.

6. Review checklist

  • ID types agree across API, forms and lookup code.
  • Use set/get rather than map[key] for Map entries.
  • Object keys require instance identity for a reason.
  • Use has() to distinguish missing entries from undefined values.
  • Duplicate handling and mutation of indexed records are defined.
  • Storage and JSON formats are explicit and validated.
  • Long-lived maps have suitable capacity or removal policies.

Checks cover different key types, object identity, undefined values, malformed IDs, duplicates and string-pair JSON. They verify example behavior, not Map-versus-object performance.

Map is most useful when its key contract is clear. Consistent IDs and explicit duplicate policy make lookups predictable before a new data structure can help.

Discussion

Comments 0

Sign in to comment

You need an account to join the discussion and reply to other readers.

Sign inRegister

No comments yet. Be the first to share your thoughts.