The jspb.Map class provides an ES6 Map-like container specifically designed for Protocol Buffer map fields. It handles key-value pairs with automatic synchronization between JavaScript Map operations and the underlying Protocol Buffer array representation.
const { Map } = require('google-protobuf');
// Create empty map
const map = new Map();
// Create map from existing array data
const existingData = [["key1", "value1"], ["key2", "value2"]];
const mapFromArray = new Map(existingData);
// Create map with value constructor (for message values)
const messageMap = new Map([], MessageConstructor);
// Basic properties
const isClean = map.arrClean; // Whether underlying array is synchronizedType Definitions:
/**
* @constructor
* @param {Array=} arr Optional initial array of [key, value] pairs
* @param {Function=} opt_valueCtor Optional constructor for map values
*/
/**
* ES6 Map-like container for Protocol Buffer map fields
*/const { Map } = require('google-protobuf');
const map = new Map();
// Set key-value pairs
map.set("name", "John Doe");
map.set("age", "25");
map.set("active", "true");
// Get values by key
const name = map.get("name"); // "John Doe"
const age = map.get("age"); // "25"
const missing = map.get("missing"); // undefined
// Check if key exists
const hasName = map.has("name"); // true
const hasEmail = map.has("email"); // false
// Delete entries
const wasDeleted = map.del("age"); // true (returns boolean)
const notDeleted = map.del("missing"); // false
// Get map size
const size = map.getLength(); // 2 (name and active remain)
// Clear all entries
map.clear();
console.log(map.getLength()); // 0Type Definitions:
/**
* @param {string} key Map key
* @param {*} value Map value
* @return {!Map} This map instance for chaining
*/
/**
* @param {string} key Map key
* @return {*} Value for key or undefined
*/const { Map } = require('google-protobuf');
const userMap = new Map();
userMap.set("john", "John Doe");
userMap.set("jane", "Jane Smith");
userMap.set("bob", "Bob Johnson");
// Iterate over entries (key-value pairs)
for (const [key, value] of map.entries()) {
console.log(`${key}: ${value}`);
}
// Iterate over keys only
for (const key of map.keys()) {
console.log(`Key: ${key}`);
}
// Iterate over values only
for (const value of map.values()) {
console.log(`Value: ${value}`);
}
// forEach method with callback
map.forEach((value, key, map) => {
console.log(`${key} => ${value}`);
});
// forEach with custom this context
const context = { prefix: "User: " };
map.forEach(function(value, key) {
console.log(this.prefix + value);
}, context);Type Definitions:
/**
* @return {!Iterator<!Array<*>>} Iterator of [key, value] pairs
*/
/**
* @param {function(*, string, !Map)} callback Function to execute for each entry
* @param {*=} opt_thisArg Value to use as 'this' when executing callback
*/const { Map } = require('google-protobuf');
const map = new Map();
map.set("id", "123");
map.set("name", "Product");
map.set("price", "99.99");
// Convert to array representation
const arrayForm = map.toArray();
// Result: [["id", "123"], ["name", "Product"], ["price", "99.99"]]
// Get entry list (for binary serialization)
const entryList = map.getEntryList();
// Returns internal array structure used for Protocol Buffer serialization
// Example: Round-trip conversion
const originalData = [["key1", "value1"], ["key2", "value2"]];
const map = new Map(originalData);
const restored = map.toArray();
console.log(JSON.stringify(originalData) === JSON.stringify(restored)); // trueconst { Map } = require('google-protobuf');
const map = new Map();
map.set("user_id", "12345");
map.set("user_name", "Alice");
map.set("user_active", "true");
// Convert to plain JavaScript object
const plainObject = map.toObject();
// Result: { user_id: "12345", user_name: "Alice", user_active: "true" }
// Convert with includeInstance flag and value transformation
const objectWithTransform = map.toObject(
true, // includeInstance - include the map instance
(value) => value.toUpperCase() // valueToObject transformation function
);
// Result: { user_id: "12345", user_name: "ALICE", user_active: "TRUE" }
// Example: Converting message value maps
const messageMap = new Map([], AddressMessage);
const homeAddress = new AddressMessage();
homeAddress.setStreet("123 Main St");
messageMap.set("home", homeAddress);
const addressObject = messageMap.toObject(false, (addr) => addr.toObject());
// Result: { home: { street: "123 Main St", ... } }Type Definitions:
/**
* @param {boolean=} includeInstance Whether to include the map instance
* @param {function(*): *=} valueToObject Function to convert values to objects
* @return {!Object} Plain JavaScript object
*/Maps with message values require special handling through value constructors:
const { Map } = require('google-protobuf');
// Define message constructor for map values
class ContactInfo extends Message {
getEmail() { return this.email_; }
setEmail(value) { this.email_ = value; }
getPhone() { return this.phone_; }
setPhone(value) { this.phone_ = value; }
}
// Create map with message value constructor
const contactMap = new Map([], ContactInfo);
// Add message entries
const johnContact = new ContactInfo();
johnContact.setEmail("john@example.com");
johnContact.setPhone("555-0123");
contactMap.set("john", johnContact);
const janeContact = new ContactInfo();
janeContact.setEmail("jane@example.com");
janeContact.setPhone("555-0456");
contactMap.set("jane", janeContact);
// Access message values
const johnInfo = contactMap.get("john");
console.log(johnInfo.getEmail()); // "john@example.com"
// Iterate over message values
contactMap.forEach((contact, name) => {
console.log(`${name}: ${contact.getEmail()}`);
});
// Convert to object form
const contactObjects = contactMap.toObject(false, (contact) => ({
email: contact.getEmail(),
phone: contact.getPhone()
}));The map maintains synchronization between its internal structure and the underlying array:
const { Map } = require('google-protobuf');
const map = new Map();
// Check synchronization status
console.log(map.arrClean); // true - initially clean
// Modify map
map.set("key1", "value1");
console.log(map.arrClean); // false - now dirty
// Array conversion triggers synchronization
const array = map.toArray();
console.log(map.arrClean); // true - clean after conversion
// Manual synchronization check
map.set("key2", "value2");
const entryList = map.getEntryList(); // Forces synchronization
console.log(map.arrClean); // true
// Example: Batch operations
const pairs = [
["name", "Alice"],
["age", "30"],
["city", "New York"]
];
// Efficient batch loading
const batchMap = new Map(pairs); // Loads directly from array
console.log(batchMap.arrClean); // true - no sync needed
// Individual additions
pairs.forEach(([key, value]) => {
batchMap.set(key, value);
});
// arrClean becomes false after modificationsconst { Map } = require('google-protobuf');
// Transform map keys and values
function transformMap(sourceMap, keyTransform, valueTransform) {
const targetMap = new Map();
sourceMap.forEach((value, key) => {
const newKey = keyTransform ? keyTransform(key) : key;
const newValue = valueTransform ? valueTransform(value) : value;
targetMap.set(newKey, newValue);
});
return targetMap;
}
// Example usage
const originalMap = new Map();
originalMap.set("user_name", "alice");
originalMap.set("user_age", "25");
const transformedMap = transformMap(
originalMap,
key => key.replace("user_", ""), // Remove prefix from keys
value => value.toString().toUpperCase() // Uppercase values
);
// Result: { name: "ALICE", age: "25" }const { Map } = require('google-protobuf');
function filterMap(sourceMap, predicate) {
const filteredMap = new Map();
sourceMap.forEach((value, key) => {
if (predicate(value, key)) {
filteredMap.set(key, value);
}
});
return filteredMap;
}
// Example: Filter out inactive users
const userMap = new Map();
userMap.set("alice", "active");
userMap.set("bob", "inactive");
userMap.set("charlie", "active");
const activeUsers = filterMap(userMap, (status) => status === "active");
// Result contains only alice and charlieconst { Map } = require('google-protobuf');
function mergeMaps(map1, map2, conflictResolver) {
const mergedMap = new Map();
// Copy all entries from first map
map1.forEach((value, key) => {
mergedMap.set(key, value);
});
// Merge entries from second map, resolving conflicts
map2.forEach((value, key) => {
if (mergedMap.has(key) && conflictResolver) {
const existing = mergedMap.get(key);
const resolved = conflictResolver(existing, value, key);
mergedMap.set(key, resolved);
} else {
mergedMap.set(key, value);
}
});
return mergedMap;
}
// Example: Merge user preferences
const defaultPrefs = new Map();
defaultPrefs.set("theme", "light");
defaultPrefs.set("notifications", "enabled");
const userPrefs = new Map();
userPrefs.set("theme", "dark");
userPrefs.set("language", "en");
const finalPrefs = mergeMaps(
defaultPrefs,
userPrefs,
(defaultVal, userVal) => userVal // User preference wins
);
// Result: { theme: "dark", notifications: "enabled", language: "en" }Maps integrate with Protocol Buffer serialization through the Message system:
const { Map, Message } = require('google-protobuf');
// Maps are typically accessed through message fields
class UserMessage extends Message {
getAttributesMap() {
return Message.getMapField(this, 1, false, null); // String map
}
getContactsMap() {
return Message.getMapField(this, 2, false, ContactMessage); // Message map
}
}
// Usage with message serialization
const user = new UserMessage();
const attrs = user.getAttributesMap();
attrs.set("role", "admin");
attrs.set("department", "engineering");
// Map is automatically serialized with the message
const serialized = user.serializeBinary();
const deserialized = UserMessage.deserializeBinary(serialized);
// Maps are preserved through serialization
const restoredAttrs = deserialized.getAttributesMap();
console.log(restoredAttrs.get("role")); // "admin"const { Map } = require('google-protobuf');
// Batch operations are more efficient than individual ones
const entries = [
["key1", "value1"],
["key2", "value2"],
["key3", "value3"]
];
// Efficient: Create map with initial data
const efficientMap = new Map(entries);
// Less efficient: Individual sets
const inefficientMap = new Map();
entries.forEach(([key, value]) => {
inefficientMap.set(key, value);
});
// Check size without triggering synchronization
if (map.getLength() > 0) {
// Process non-empty map
}
// Minimize conversions
const arrayOnce = map.toArray(); // Cache if needed multiple times// Clear large maps when done
largeMap.clear();
// Avoid holding references to cleared maps
largeMap = null;
// Reuse maps for similar data structures
function processUserBatch(users) {
const tempMap = new Map();
users.forEach(user => {
tempMap.clear(); // Reuse same map instance
// Populate with user data
tempMap.set("id", user.id);
tempMap.set("name", user.name);
// Process map
processUserMap(tempMap);
});
}