or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

binary-io.mdbytestring-utilities.mddebug-utilities.mdextension-fields.mdindex.mdmap-operations.mdmessage-operations.md
tile.json

map-operations.mddocs/

Map Operations

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.

Core Map Class { .api }

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 synchronized

Type 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
 */

Basic Map Operations { .api }

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());       // 0

Type 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
 */

Iteration Methods { .api }

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
 */

Conversion Methods { .api }

Array Conversion

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)); // true

Object Conversion

const { 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
 */

Message Value Maps { .api }

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()
}));

Internal Synchronization { .api }

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 modifications

Advanced Usage Patterns { .api }

Map Transformation

const { 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" }

Map Filtering

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 charlie

Map Merging

const { 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" }

Serialization Integration { .api }

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"

Performance Considerations

Efficient Operations

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

Memory Management

// 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);
  });
}