Skip to content

D3.js - Collections API

D3.js - Working with Data Structures (Collections)

Section titled “D3.js - Working with Data Structures (Collections)”

Effectively working with data is central to D3.js. While D3 previously had its own collection types like d3.map and d3.set, modern JavaScript provides native Map and Set objects. D3.js now encourages using these native structures, along with standard JavaScript object and array methods. The d3-array module (part of the main D3 bundle) offers powerful utilities for array manipulation, grouping, and aggregation.

Most collection-related utilities, especially from d3-array, are included in the main D3.js library.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3.js Collections Example</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div id="output"></div>
<script>
// Your D3.js and data manipulation code will go here
</script>
</body>
</html>

D3.js seamlessly integrates with JavaScript’s built-in data structures.

Standard JavaScript objects are commonly used to represent individual data items. You can access their properties using dot notation or bracket notation.

Modern JavaScript provides built-in methods to work with object properties:

  • Object.keys(object): Returns an array of a given object’s own enumerable property names (keys).
  • Object.values(object): Returns an array of a given object’s own enumerable property values.
  • Object.entries(object): Returns an array of a given object’s own enumerable string-keyed property [key, value] pairs.

Example:

const monthData = { "jan": 1, "feb": 2, "mar": 3, "apr": 4 };
console.log(Object.keys(monthData)); // Output: ["jan", "feb", "mar", "apr"]
console.log(Object.values(monthData)); // Output: [1, 2, 3, 4]
console.log(Object.entries(monthData));
// Output: [["jan", 1], ["feb", 2], ["mar", 3], ["apr", 4]]
// To display in HTML (example)
document.getElementById('output').innerHTML += `<p>Keys: ${Object.keys(monthData).join(', ')}</p>`;

JavaScript’s native Map object holds key-value pairs and remembers the original insertion order of the keys. Keys can be of any type (not just strings). This is generally preferred over D3’s historical d3.map.

  • new Map(): Creates a new Map.
  • map.set(key, value): Adds or updates an entry.
  • map.get(key): Retrieves the value for a key.
  • map.has(key): Checks if a key exists.
  • map.delete(key): Removes an entry.
  • map.clear(): Removes all entries.
  • map.size: Returns the number of entries.
  • map.keys(), map.values(), map.entries(): Return iterators for keys, values, or entries.

Example:

const cityPopulations = new Map();
cityPopulations.set("New York", 8399000);
cityPopulations.set("London", 8982000);
console.log(cityPopulations.get("London")); // Output: 8982000
console.log(cityPopulations.has("Paris")); // Output: false
console.log(cityPopulations.size); // Output: 2
// Iterating over a Map (often converted to an array for D3 data joins)
// const mapEntriesArray = Array.from(cityPopulations.entries());
// Or for D3 data binding, you might use Array.from(cityPopulations) which gives [[key, value], ...]
let mapOutput = "<p>City Populations (Map):</p><ul>";
for (const [city, population] of cityPopulations) {
mapOutput += `<li>${city}: ${population.toLocaleString()}</li>`;
}
mapOutput += "</ul>";
document.getElementById('output').innerHTML += mapOutput;

JavaScript’s native Set object lets you store unique values of any type. This is preferred over D3’s historical d3.set.

  • new Set(): Creates a new Set.
  • set.add(value): Adds a value.
  • set.has(value): Checks if a value exists.
  • set.delete(value): Removes a value.
  • set.clear(): Removes all values.
  • set.size: Returns the number of values.
  • set.values() (or iterate directly on the set): Returns an iterator for values.

Example:

const fruitSet = new Set();
fruitSet.add("apple").add("banana").add("apple"); // "apple" is added only once
console.log(fruitSet.has("orange")); // Output: false
console.log(fruitSet.size); // Output: 2 (apple, banana)
// const fruitArray = Array.from(fruitSet); // Useful for D3 data joins
let setOutput = "<p>Unique Fruits (Set):</p><ul>";
for (const fruit of fruitSet) {
setOutput += `<li>${fruit}</li>`;
}
setOutput += "</ul>";
document.getElementById('output').innerHTML += setOutput;

The d3-array module provides powerful functions for data transformation, notably d3.group() and d3.rollup(), which replace the older d3.nest() functionality.

d3.group() groups items from an iterable (e.g., an array of objects) by one or more key functions. It returns a JavaScript Map. If multiple key functions are provided, it creates nested Maps.

Example: Grouping data by a property.

const data = [
{ category: "A", value: 10 },
{ category: "B", value: 20 },
{ category: "A", value: 30 },
{ category: "C", value: 40 },
{ category: "B", value: 50 }
];
// Group by 'category'
const groupedByCategory = d3.group(data, d => d.category);
// groupedByCategory is a Map:
// Map {
// "A" => [ { category: "A", value: 10 }, { category: "A", value: 30 } ],
// "B" => [ { category: "B", value: 20 }, { category: "B", value: 50 } ],
// "C" => [ { category: "C", value: 40 } ]
// }
console.log(groupedByCategory.get("A"));
// Output: [ { category: "A", value: 10 }, { category: "A", value: 30 } ]
// To convert to an array of {key, values} objects (similar to d3.nest().entries()):
const groupedArray = Array.from(groupedByCategory, ([key, values]) => ({ key, values }));
console.log(groupedArray);
/* Output:
[
{ key: "A", values: [ { category: "A", value: 10 }, { category: "A", value: 30 } ] },
{ key: "B", values: [ { category: "B", value: 20 }, { category: "B", value: 50 } ] },
{ key: "C", values: [ { category: "C", value: 40 } ] }
]
*/

d3.rollup() is similar to d3.group(), but it also applies a reduce function to the values in each group, returning the reduced value instead of the array of values. This is useful for aggregations like sum, count, average, etc.

Example: Summing values by category.

const dataForRollup = [
{ category: "A", value: 10 },
{ category: "B", value: 20 },
{ category: "A", value: 30 },
{ category: "C", value: 40 },
{ category: "B", value: 50 }
];
// Sum values for each category
const sumByCategory = d3.rollup(
dataForRollup,
v => d3.sum(v, d => d.value), // Reduce function: sum the 'value' property of items in group 'v'
d => d.category // Key function: group by 'category'
);
// sumByCategory is a Map:
// Map {
// "A" => 40, (10 + 30)
// "B" => 70, (20 + 50)
// "C" => 40
// }
console.log(sumByCategory.get("A")); // Output: 40
// To convert to an array of {key, value} (where value is the rolled-up result):
const rolledUpArray = Array.from(sumByCategory, ([key, value]) => ({ key, value }));
console.log(rolledUpArray);
/* Output:
[
{ key: "A", value: 40 },
{ key: "B", value: 70 },
{ key: "C", value: 40 }
]
*/

By leveraging native JavaScript data structures and powerful utilities from d3-array, you can efficiently manage and prepare your data for visualization with D3.js.