1console.log("hello-world);
javascript
console.log("hello-world);
hidden codeblock for CSS
css
.container {
  width: 80%;
}
hidden codeblock for CSS
html
<pre><code class="language-css">
.container {
  width: 80%;
}
</code></pre>
hidden codeblock for CSS
1console.log("hello-world);
javascript
console.log("hello-world);
hidden codeblock for CSS
css
.container {
  width: 80%;
}
hidden codeblock for CSS
html
<pre><code class="language-css">
.container {
  width: 80%;
}
</code></pre>
hidden codeblock for CSS

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

ReadTime: 15 minutes

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.
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"]]
Map creation and APIs (JavaScript)

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.

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"]]
Spread Operator to convert Map to Array (JavaScript)

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().

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"]]
Array.from() to convert Map to Array (JavaScript)

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.

javascript
const keyArray = [];
const valueArray = [];

myMap.forEach((value, key) => {
  keyArray.push(key);
  valueArray.push(value);
});
forEach() and push() to convert Map to Array (JavaScript)

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.

javascript
const keyArray = [];
const valueArray = [];

for (let [key, value] of myMap) {
  keyArray.push(key);
  valueArray.push(value);
}
for…of and push() to convert Map to Array (JavaScript)

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.

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]
// ]
Nested Map to Array Conversion (JavaScript)
  • 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.

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']
Non String Key Map to Array Conversion (JavaScript)

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.

javascript
let keyValueArray = [['key1', 'value1'], ['key2', 'value2']];

let reconstructedMap = new Map(keyValueArray);

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

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.

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"
// }
Array to Map (Complex) (JavaScript)
  • 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.

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
Map Iteration Order Ensured (JavaScript)

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.

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'
No Link Between Converted Array And Map (JavaScript)

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

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
Chunking to convert Large Map to Array (Javascript)

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:

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"]]
Lazy Loading to convert Large Map to Array (Javascript)
  • 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):

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

Then, in your main script:

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]);
}
Main Script \`worker.js\` (JavaScript)
  • 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)

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" }]
Problem Code

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;
}
Solution Code

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! 🚀👨‍💻

Contact

Feel free to reach out!