How to Convert JavaScript Map Keys, Values, Entries to Arrays (and the reverse)


Today, we will dive deep into converting a Mapโ€™s to keys, values and entries (key-value pair) to their Arrays in JavaScript. And the reverse. We will learn these techniques for simple maps, and even complex Maps and arrays (having nested data structures). Then weโ€™ll see some performance tips for very large maps (Chunking, Lazy Loading, Web Workers). Ready to dive in?


Refresher: Maps in JavaScript:

JavaScript offers a variety of ways to store and manage data. Two such are Maps and Arrays.

  • JavaScript Maps: Introduced in ES6, Maps are collections that store key-value pairs. They can have keys of any data type and maintain the order of insertion, which is a significant advantage over Objects.
  • Maps can be constructed using the set method, which can be chained for multiple entries. Or by passing two dimensional array of key value pairs in constructor.
  • map.keys(), map.values(), and map.entries() methods return iterators for keys, values, and key-value pairs respectively. These can be converted to arrays using the spread operator (...), or Array.from(). We will see these next.
  • Map.prototype.has() and Map.prototype.delete(): usefult for manipulating and querying Maps.
    • has(): Checks if a key exists in the Map.
    • delete(): Removes a key-value pair from the Map.
Map creation and APIs (JavaScript)
// Map Construction:
// 1๏ธโƒฃ Constructor initialisation with 2 dimensional Array:
let myMap = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);
console.log(myMap); // Map(2) { 'key1' => 'value1', 'key2' => 'value2' }

// 2๏ธโƒฃ Chained construction using set: Chaining set to initialise Map with multiple Key-Value pairs.
let chainedMap = new Map().set('apple', 1).set('banana', 2);
console.log(chainedMap); // Map(2) {"apple" => 1, "banana" => 2}

// Retrieving keys, values, and entries
console.log([...myMap.keys()]); // ["key1", "key2"]
console.log([...myMap.values()]); // ["value1", "value2"]
console.log([...myMap.entries()]); // [["key1", "value1"], ["key2", "value2"]]

// has():
if (myMap.has('key1')) {
  console.log('Key1 exists');
}
// Output: 'Key1 exists'

// delete():
myMap.delete('key2');
console.log([...myMap.entries()]); 
// Output: [["key1", "value1"]]

Ways to Convert Map key, values, entries to Array in Javascript

Using the Spread Operator:

keys(), values() or entries() give us iterators to the Map keys, values and entries (key-value pairs. We can use the spread operator ... to convert get the array from the iterator.

Spread Operator to convert Map to Array (JavaScript)
let myMap = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);

let keysArray = [...myMap.keys()];
let valuesArray = [...myMap.values()];
let entriesArray = [...myMap.entries()];

console.log(keysArray); // ["key1", "key2"]
console.log(valuesArray); // ["value1", "value2"]
console.log(entriesArray); // // [["key1", "value1"], ["key2", "value2"]]

Using Array.from()

Similar to spread operators, we can convert the iterators obtained from keys(), values() or entries() to their respective arrays using Array.from().

Array.from() to convert Map to Array (JavaScript)
let myMap = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);

let keysArray = Array.from(myMap.keys());
let valuesArray = Array.from(myMap.values());
let entriesArray = Array.from(myMap.entries());

console.log(keysArray); // ["key1", "key2"]
console.log(valuesArray); // ["value1", "value2"]
console.log(entriesArray); // // [["key1", "value1"], ["key2", "value2"]]

Using Loops:

Using forEach() and push():

The Map.forEach() method provides control while pushing each key, value into an array. But it wonโ€™t allow for break and continue.

forEach() and push() to convert Map to Array (JavaScript)
const keyArray = [];
const valueArray = [];

myMap.forEach((value, key) => {
  keyArray.push(key);
  valueArray.push(value);
});

Utilizing the forโ€ฆof Loop:

The for...of loop is an alternative iteration method that also provides direct access to the Mapโ€™s entries while adding to Arrays. And allows to break and continue. In following code we Destructure each Map key-value pair and add them in respective arrays.

forโ€ฆof and push() to convert Map to Array (JavaScript)
const keyArray = [];
const valueArray = [];

for (let [key, value] of myMap) {
  keyArray.push(key);
  valueArray.push(value);
}

Advanced Techniques for Map to Array Conversions

Handling Nested Maps Structures

When converting a Map to an Array in JavaScript, handling nested structures (including nested Maps, Arrays, Objects, Numbers, and Strings) requires a recursive approach. This ensures that all nested elements are properly processed and included in the final array.

Nested Map to Array Conversion (JavaScript)
function convertMapToArray(map) {
  const result = [];

  function processEntry(key, value) {
    if (value instanceof Map) {
      // For nested Maps, recursively process each entry
      value.forEach((v, k) => processEntry(k, v));
    } else {
      // For other types (Array, Object, Number, String), add directly
      result.push([key, value]);
    }
  }

  map.forEach((value, key) => processEntry(key, value));
  return result;
}

// Example usage with a complex Map
const complexMap = new Map([
  ['key1', 'value1'],
  ['nestedMap', new Map([['nestedKey', 'nestedValue']])],
  ['arrayKey', [1, 2, 3]],
  ['objectKey', { prop: 'value' }],
  ['numericKey', 42]
]);

let arrayFromComplexMap = convertMapToArray(complexMap);
console.log(arrayFromComplexMap);
// Output: ๐Ÿ‘‡
// [
//   ["key1", "value1"], 
//   ["nestedKey", "nestedValue"], 
//   ["arrayKey", [1, 2, 3]], 
//   ["objectKey", { prop: "value" }], 
//   ["numericKey", 42]
// ]
  • The convertMapToArray function initializes an empty array (result) to store the converted key-value pairs.
  • It defines an inner function processEntry that takes a key and a value. This function checks if the value is a Map:
    • If it is, the function calls itself recursively for each entry in the nested Map. This ensures that nested Maps are fully traversed and their contents added to the result.
    • If the value is not a Map (i.e., an Array, Object, Number, or String), the key-value pair is added directly to the result array.
  • The main part of the function iterates over each entry in the provided Map and uses processEntry to process it.
  • The result is an array that includes both the top-level and nested Map entries, as well as other data types, properly flattened.

Handling Maps with Non String Keys

Maps in JavaScript can have keys of any data type (not just string as in case of Objects).

If you only want to convert a Map key, value, or entries to array, then it shouldnโ€™t be a problem that the Map has non string keys. Using Spread operator with map.keys(), map.values(), or map.entries(), or using Array.from(map) should still work.

When you convert a Map to an Array, you are typically creating an array of key-value pairs, and these pairs can capture any data type as a key without the need for conversion or serialization.

Non String Key Map to Array Conversion (JavaScript)
let myMap = new Map();
myMap.set('stringKey', 'value1');
myMap.set(123, 'value2'); // Non-string key
myMap.set({ id: 3 }, 'value3'); // Object as a key

// Convert Map entries to an Array
let entriesArray = [...myMap.entries()];
console.log(entriesArray);
// Output: [['stringKey', 'value1'], [123, 'value2'], [{ id: 3 }, 'value3']]

// Convert Map keys to an Array
let keysArray = [...myMap.keys()];
console.log(keysArray);
// Output: ['stringKey', 123, { id: 3 }]

// Convert Map values to an Array
let valuesArray = [...myMap.values()];
console.log(valuesArray);
// Output: ['value1', 'value2', 'value3']

The concern about non-string keys is more relevant when converting a Map to an Object, particularly because Objects in JavaScript can only have strings or symbols as keys.

Converting Arrays Back to Maps in JavaScript

Well depends on what does you Array has. Are they simple entries like String, Number, etc, or are they Object, Arrays, or other Maps as entries. Lets see both.

For Simple Maps

To convert an Array back to a Map, itโ€™s essential that the Array is structured in a way that is compatible with the Map constructor. The most straightforward scenario is when the Array consists of nested arrays, each representing a key-value pair, like [[key1, value1], [key2, value2], ...]. And that each entry is just string, number, etc. (i.e. not a further nested data structure). Then we can convert using just a Map constructor, as follows.

Array to Map (Simple) (JavaScript)
let keyValueArray = [['key1', 'value1'], ['key2', 'value2']];

let reconstructedMap = new Map(keyValueArray);

console.log(reconstructedMap);
// Output: Map(2) {"key1" => "value1", "key2" => "value2"}

For Complex Maps

Now, what if we have a complex array with mixed data types and nested structures, and we want to converting it into a Map.

Essentially, we identify and handle each type of element, ensuring they are appropriately set as key-value pairs in the resulting Map. Lets see this in action in below code example.

Suppose you have an array (complexArray) comprising various data types, including nested structures. The aim is to process this array and create a complexMap where each appropriate element or set of elements becomes a Map entry.

Array to Map (Complex) (JavaScript)
let complexArray = [
  'key1', 'value1',
  ['nestedArrayKey', ['nested', 'array']],
  'key2', 42,
  { objKey: 'objValue' },
  new Map([['mapKey', 'mapValue']])
];

let complexMap = new Map();
let tempKey = null;

complexArray.forEach(item => {
  if (Array.isArray(item)) {
    // Array item is treated as a key-value pair
    complexMap.set(item[0], item[1]);
  } else if (item instanceof Map) {
    // Map items are iterated and added to the complexMap
    item.forEach((value, key) => complexMap.set(key, value));
  } else if (typeof item === 'object') {
    // Object entries are added to the complexMap
    Object.entries(item).forEach(([key, value]) => complexMap.set(key, value));
  } else {
    // Handling primitive types as keys or values
    if (tempKey === null) {
      tempKey = item; // Storing the key
    } else {
      complexMap.set(tempKey, item); // Setting the key-value pair
      tempKey = null; // Resetting tempKey for the next pair
    }
  }
});

console.log(complexMap);
// Output: ๐Ÿ‘‡
// Map(5) {
//    "key1" => "value1", 
//    "nestedArrayKey" => ["nested", "array"], 
//    "key2" => 42, 
//    "objKey" => "objValue", 
//    "mapKey" => "mapValue"
// }
  • The code iterates over complexArray, processing each item based on its type.
  • Arrays: Detected using Array.isArray(item) and treated as key-value pairs.
  • Maps: Identified with item instanceof Map. Each entry in these nested Maps is added to complexMap.
  • Objects: Detected with typeof item === 'object'. Object entries are added to complexMap.
  • Primitive Types (String, Number): Handled as potential keys or values. A temporary variable (tempKey) is used to store a key until its corresponding value is found.
  • The complexMap gets populated with a mix of entries derived from the diverse elements in complexArray.

Tips on Javascript Map to Object Conversion

Iteration Order ensured in order of keys

While the iteration order for Objects is now consistent in modern JavaScript engines, it wasnโ€™t historically guaranteed. Maps, however, always maintain the insertion order.

Map Iteration Order Ensured (JavaScript)
let myObject = { key1: 'value1', key2: 'value2', key3: 'value3' };
let myMap = new Map(Object.entries(myObject));

console.log(Object.keys(myObject)); 
// Output may vary, e.g., ["key1", "key2", "key3"]

console.log([...myMap.keys()]); 
// ["key1", "key2", "key3"], order is guaranteed

No Link Between Array And Map After Conversion

JavaScript doesnโ€™t offer a native way to create a live link between a Map and an Array. So once converted, the Array has no linkage to the Map.

No Link Between Converted Array And Map (JavaScript)
let myMap = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);

// Convert Map keys to an Array
let keysArray = [...myMap.keys()];
console.log(keysArray); // ["key1", "key2"]

// Adding a new key-value pair to the Map
myMap.set('key3', 'value3');

// The keysArray remains unchanged
console.log(keysArray); // Still ["key1", "key2"], not updated with 'key3'

Performance Tips

  • Are you converting reaallly large Maps to Arrays?
  • Can you get away without conversion? ๐Ÿ˜
  • No? Can you minimise minimize the number of conversions.
  • If still issues, then explore following 3 techniques for really large Maps: Chunking, LazyLoading and WebWorkers.

Chunking

Chunking to convert Large Map to Array (Javascript)
function chunkMapToArray(map, chunkSize) {
  const array = [];
  let count = 0;
  let tempArray = [];

  for (let [key, value] of map) {
    tempArray.push([key, value]);
    count++;

    if (count % chunkSize === 0) {
      array.push(...tempArray);
      tempArray = [];
    }
  }

  // Add any remaining items
  if (tempArray.length > 0) {
    array.push(...tempArray);
  }

  return array;
}

// Example usage
let largeMap = new Map([...Array(100).keys()].map(k => [k, `value${k}`]));
let chunkedArray = chunkMapToArray(largeMap, 10);
console.log(chunkedArray);
// Output: Array of 100 entries, chunked in groups of 10

If you want to convert a large Map with thousands of entries to an Array, then processing it all at once can lead to performance issues. Instead, we can chunk the Map into smaller parts and convert each chunk to an Array sequentially. See below code:

  • The chunkMapToArray function divides the Map into chunks.
  • It iterates over the Map, accumulating entries into tempArray.
  • Once the tempArray reaches the size of chunkSize, its contents are pushed into the main array.
  • After processing all entries, any remaining items in tempArray are added to array.

Lazy Loading

Suppose you have a (large) Map representing user data, but only need to display a subset at any given time. Lazy loading allows you to convert only the relevant part of the Map. See the code below:

Lazy Loading to convert Large Map to Array (Javascript)
function lazyConvertMapToArray(map, keysToLoad) {
  const array = [];
  for (let key of keysToLoad) {
    if (map.has(key)) {
      array.push([key, map.get(key)]);
    }
  }
  return array;
}

// Example usage
let userDataMap = new Map([...Array(1000).keys()].map(k => [k, `user${k}`]));
let keysToLoad = [100, 200, 300];
let partialArray = lazyConvertMapToArray(userDataMap, keysToLoad);
console.log(partialArray);
// Output: [[100, "user100"], [200, "user200"], [300, "user300"]]
  • lazyConvertMapToArray takes a Map and an array of keys to load.
  • It iterates over keysToLoad, adding entries to array if the key exists in the Map.

Web Workers

In a web application, converting a very large Map to an Array in the main thread could lead to a sluggish UI. Using a Web Worker, this task can be performed in the background.

Hereโ€™s an example demonstrating how to use a Web Worker for converting a large Map to Array.

First, create a Web Worker script (worker.js):

Web Worker Script `worker.js` (JavaScript)
// worker.js
self.onmessage = function(e) {
  const mapData = new Map(e.data);
  const array = Array.from(mapData);
  self.postMessage(array);
};

Then, in your main script:

Main Script `worker.js` (JavaScript)
// Main JavaScript file
if (window.Worker) {
  const myWorker = new Worker('worker.js');

  myWorker.onmessage = function(e) {
    console.log('Converted Array:', e.data);
  };

  const largeMap = new Map([...Array(1000).keys()].map(k => [k, `value${k}`]));
  myWorker.postMessage([...largeMap]);
}
  • The Web Worker script (worker.js) listens for messages containing Map data, converts it to an Array, and sends it back.
  • In the main script, first we check support for WebWorkers using window.Worker.
  • window.Worker: The Worker object is a part of the global scope, typically accessed through the window object in browsers. Checking if (window.Worker) essentially checks if the Worker constructor is available, indicating support for Web Workers.
  • If worker is supported, then the Worker is instantiated, and the Map data is sent to it for processing.
  • The Worker processes the Map in the background and posts the converted Array back to the main thread.

๐ŸงชPractice Coding Problem: Complex Map Flattener

In the spirit of Test Driven Development ( ๐Ÿ˜), lets test our understanding by solving a problem.

Create a function that transforms a complex Map into a single array. This Map may include nested structures such as other Maps, Arrays, or Objects. Each entry in the resulting array should be a flattened combination of keys and their corresponding values from the Map, including the nested structures.

See the following driver code for better understanding.

Problem (JavaScript)
/**
 * Flattens a complex Map containing nested Maps, Arrays, or Objects into a single array.
 * Each entry in the array is a combination of keys and values from the Map.
 * @param {Map} map - The complex Map to be flattened.
 * @return {Array} - An array containing flattened key-value pairs.
 */
function complexMapFlattener(map) {
  // Your code here
}

const complexMap = new Map([
  ['simpleKey', 'simpleValue'],
  ['nestedMap', new Map([['nestedKey', 'nestedValue']])],
  ['arrayKey', [1, 2, 3]],
  ['objectKey', { prop: 'value' }]
]);

console.log(complexMapFlattener(complexMap)); 
// Expected Output: ["simpleKey", "simpleValue", "nestedMap", "nestedKey", "nestedValue", "arrayKey", [1, 2, 3], "objectKey", { prop: "value" }]
Please attempt before seeing the Answer:
Solution (JavaScript)
function complexMapFlattener(map) {
  const result = [];

  for (let [key, value] of map) {
    result.push(key);
    if (value instanceof Map) {
      for (let [nestedKey, nestedValue] of value) {
        result.push(nestedKey, nestedValue);
      }
    } else {
      result.push(value);
    }
  }

  return result;
}

Explanation:

  • The complexMapFlattener function iterates over the entries of the provided Map.
  • For each entry, the key is first added to the result array.
  • If the value is an instance of Map (indicating a nested structure), the function further iterates over this nested Map. Each key and value from the nested Map is then added to the result array.
  • If the value is not a Map (e.g., an Array, Object, or primitive), it is added directly to the result array.
  • This process flattens the complex Map structure into a single array, combining keys and values while preserving the order and structure of nested Maps.

With these techniques and tips, you can confidently handle Array-fy your Map & Mapify your arrays.

"Why was the JavaScript developer sad? 
Because they couldn't find the right key to their Map's happiness... until they array-fied it!" ๐Ÿ˜€

Keep your key to happiness, and keep coding! ๐Ÿš€๐Ÿ‘จโ€๐Ÿ’ป

Scroll to Top