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.
Setting up D3.js
Section titled “Setting up D3.js”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>JavaScript’s Native Data Structures
Section titled “JavaScript’s Native Data Structures”D3.js seamlessly integrates with JavaScript’s built-in data structures.
Objects
Section titled “Objects”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>`;Maps (Native Map)
Section titled “Maps (Native Map)”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: 8982000console.log(cityPopulations.has("Paris")); // Output: falseconsole.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;Sets (Native Set)
Section titled “Sets (Native Set)”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: falseconsole.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;Grouping and Aggregating with d3-array
Section titled “Grouping and Aggregating with d3-array”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(iterable, ...keys)
Section titled “d3.group(iterable, ...keys)”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(iterable, reduce, ...keys)
Section titled “d3.rollup(iterable, reduce, ...keys)”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 categoryconst 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.