How Lodash Transform Modifies Complex Map Objects
When Lodash’s _.transform method processes a JavaScript
Map, it alters the internal and enumerable structure of the
data rather than performing a standard deep collection iteration.
Because _.transform is designed around plain JavaScript
objects and arrays, passing a Map causes unexpected
prototype binding without the allocation of native internal slots,
filters out non-enumerable Map entries, and produces an object that
mimics the prototype chain of a Map while fundamentally
breaking native Map operations.
Accumulator Instantiation and Prototype Inheritance
When _.transform is invoked on an object without an
explicit accumulator, it inspects the target constructor and
prototype:
accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {};When evaluating a native Map,
object.constructor resolves to Map. Instead of
executing new Map(), Lodash calls its internal
baseCreate method, effectively running
Object.create(Map.prototype).
This applies a specific structural change:
- The accumulator becomes a generic object inheriting from
Map.prototype. - It lacks the native
[[MapData]]internal slot required by the ECMAScript specification forMapinstances. - Any downstream attempt within the iteratee to use native methods
such as
accumulator.set(key, value)throws aTypeError: Method Map.prototype.set called on incompatible receiver.
Traversal Mechanics via
baseForOwn
_.transform divides collections into two categories:
array-like objects and general objects. Because a Map does
not qualify as array-like, the iteration defaults to
baseForOwn:
baseForOwniterates exclusively over the own enumerable string and symbol properties of the object viaObject.keysor equivalent iteration routines.- The internal key-value entries of the
Mapstored in[[MapData]]are completely ignored. - If the complex
Maphas standard entries added viamap.set('key', 'value'), the iteratee does not execute for these pairs. - The iteratee executes only if arbitrary custom properties were
directly attached to the Map instance (e.g.,
map.customProperty = 'meta').
Structural Modifications on Complex, Nested Maps
When processing a complex Map (such as a nested
structure or one containing arbitrary attached properties), the exact
transformations occur as follows:
- Loss of Native Map Entries: Any sub-maps, objects,
or primitive entries held inside the
Mapvia.set()are dropped from the transformation process entirely. - Flattening of Custom Properties: Any monkey-patched properties attached as own-enumerable keys are visited. If the iteratee assigns these properties to the default accumulator, they are attached as standard properties on the newly created object.
- Internal Reference Decoupling: Because the default
accumulator inherits
Map.prototypewithout instance initialization, the resulting object retainsMapmethods along its prototype chain (get,set,has,clear), but all of them are permanently non-functional on that instance. - Explicit Accumulator Deviation: If an initialized
instance—such as a valid
new Map()—is explicitly supplied as the third argument to_.transform,baseForOwnstill governs the source iteration. The target will only receive modifications driven by the source's own enumerable properties, leaving the native entries of the source unread.
Transformation Summary
Directly applying _.transform to an ES6 Map
reduces the complex map to a hollow prototype wrapper. It strips native
key-value storage, ignores internal collections, transforms own
properties into standard object keys on the accumulator, and severs
compatibility with the ECMAScript Map API. To preserve map entries
during transformation, the Map must first be converted to
an array via Array.from(map.entries()) or iterated using
native Map.prototype.forEach.