Working on Jurassic Park management systems, we often need to store collections of unique values or map keys to values. In JavaScript, before ES6, we typically used objects or arrays for these purposes, but they came with certain limitations. ECMAScript 6 introduced two new data structures that solve many of these problems:
Map and Set.Set is a collection of unique values — each value can only appear once. This is an excellent solution when we need to store a set of elements without duplicates.1// Creating an empty set
2const dinosaurs = new Set();
3
4// Creating a set with initial values
5const carnivores = new Set(['Tyrannosaurus', 'Velociraptor', 'Spinosaurus', 'Allosaurus']);
6
7// Creating a set from an array (automatically removes duplicates)
8const parkDinosaurs = new Set([
9 'Tyrannosaurus',
10 'Triceratops',
11 'Velociraptor',
12 'Stegosaurus',
13 'Tyrannosaurus', // Duplicate - will be skipped
14 'Brachiosaurus',
15 'Velociraptor' // Duplicate - will be skipped
16]);
17
18console.log(parkDinosaurs.size); // 5 - duplicates were removed
19console.log([...parkDinosaurs]); // Convert to array
20// ['Tyrannosaurus', 'Triceratops', 'Velociraptor', 'Stegosaurus', 'Brachiosaurus']1// Adding elements to the set
2dinosaurs.add('Tyrannosaurus');
3dinosaurs.add('Velociraptor');
4dinosaurs.add('Triceratops');
5
6// Attempting to add a duplicate - will be ignored
7dinosaurs.add('Tyrannosaurus');
8
9console.log(dinosaurs.size); // 3
10
11// Checking if an element exists in the set
12console.log(dinosaurs.has('Velociraptor')); // true
13console.log(dinosaurs.has('Brachiosaurus')); // false
14
15// Removing an element from the set
16dinosaurs.delete('Triceratops');
17console.log(dinosaurs.size); // 2
18
19// Clearing the entire set
20dinosaurs.clear();
21console.log(dinosaurs.size); // 01const dinosaurSpecies = new Set([
2 'Tyrannosaurus',
3 'Triceratops',
4 'Velociraptor',
5 'Stegosaurus',
6 'Brachiosaurus'
7]);
8
9// Iteration using for...of loop
10console.log('Dinosaur species in the park:');
11for (const species of dinosaurSpecies) {
12 console.log(`- ${species}`);
13}
14
15// Using forEach method
16dinosaurSpecies.forEach(species => {
17 console.log(`Species: ${species}`);
18});
19
20// Converting Set to array
21const speciesArray = [...dinosaurSpecies];
22console.log('Array of species:', speciesArray);
23
24// Iteration with access to indices (through array conversion)
25[...dinosaurSpecies].forEach((species, index) => {
26 console.log(`${index + 1}. ${species}`);
27});1// Array with repeating values
2const dinoSightings = [
3 'Tyrannosaurus', 'Velociraptor', 'Triceratops', 'Tyrannosaurus',
4 'Stegosaurus', 'Velociraptor', 'Brachiosaurus', 'Tyrannosaurus'
5];
6
7// Removing duplicates using Set
8const uniqueDinoSightings = [...new Set(dinoSightings)];
9
10console.log('Unique species observed in the park:');
11console.log(uniqueDinoSightings);
12// ['Tyrannosaurus', 'Velociraptor', 'Triceratops', 'Stegosaurus', 'Brachiosaurus']1// Cataloging unique dinosaur characteristics
2const dinoFeatures = [
3 { species: 'Tyrannosaurus', features: ['sharp teeth', 'small arms', 'powerful jaws'] },
4 { species: 'Velociraptor', features: ['sharp claws', 'fast runner', 'pack hunter'] },
5 { species: 'Triceratops', features: ['three horns', 'neck frill', 'herbivore'] },
6 { species: 'Stegosaurus', features: ['back plates', 'tail spikes', 'herbivore'] }
7];
8
9// Set of unique features from all dinosaurs
10const allFeatures = new Set();
11
12dinoFeatures.forEach(dino => {
13 dino.features.forEach(feature => allFeatures.add(feature));
14});
15
16console.log('All unique dinosaur features:');
17console.log([...allFeatures]);
18// ['sharp teeth', 'small arms', 'powerful jaws', 'sharp claws', 'fast runner', 'pack hunter', 'three horns', 'neck frill', 'herbivore', 'back plates', 'tail spikes']
19
20// Counting dinosaurs that are herbivores
21const herbivoreCount = dinoFeatures.filter(dino =>
22 dino.features.includes('herbivore')
23).length;
24
25console.log(`Number of herbivore species: ${herbivoreCount}`); // 2We can implement set theory operations such as union, difference, or intersection:
1// Dinosaurs in the first enclosure
2const paddockA = new Set(['Tyrannosaurus', 'Velociraptor', 'Dilophosaurus']);
3
4// Dinosaurs in the second enclosure
5const paddockB = new Set(['Triceratops', 'Stegosaurus', 'Velociraptor', 'Gallimimus']);
6
7// Union — all unique dinosaurs from both enclosures
8const allDinosaurs = new Set([...paddockA, ...paddockB]);
9console.log('All dinosaurs:', [...allDinosaurs]);
10// ['Tyrannosaurus', 'Velociraptor', 'Dilophosaurus', 'Triceratops', 'Stegosaurus', 'Gallimimus']
11
12// Difference — dinosaurs in the first enclosure but not the second
13const uniqueToPaddockA = new Set(
14 [...paddockA].filter(dino => !paddockB.has(dino))
15);
16console.log('Dinosaurs unique to paddock A:', [...uniqueToPaddockA]);
17// ['Tyrannosaurus', 'Dilophosaurus']
18
19// Intersection — dinosaurs present in both enclosures
20const commonDinosaurs = new Set(
21 [...paddockA].filter(dino => paddockB.has(dino))
22);
23console.log('Dinosaurs present in both enclosures:', [...commonDinosaurs]);
24// ['Velociraptor']Map is a collection of key-value pairs where keys can be of any type (not just strings, as in plain objects). A Map preserves the order of element insertion and enables easy iteration.1// Creating an empty map
2const dinoStats = new Map();
3
4// Creating a map with initial key-value pairs
5const dinoHeights = new Map([
6 ['Tyrannosaurus', 5.6],
7 ['Velociraptor', 1.8],
8 ['Triceratops', 3.0],
9 ['Stegosaurus', 4.0],
10 ['Brachiosaurus', 13.0]
11]);
12
13console.log(dinoHeights.size); // 51// Adding key-value pairs
2dinoStats.set('Tyrannosaurus', { height: 5.6, weight: 7000, diet: 'carnivore' });
3dinoStats.set('Velociraptor', { height: 1.8, weight: 15, diet: 'carnivore' });
4dinoStats.set('Triceratops', { height: 3.0, weight: 6000, diet: 'herbivore' });
5
6// Getting a value by key
7const trexStats = dinoStats.get('Tyrannosaurus');
8console.log('T-Rex stats:', trexStats);
9// { height: 5.6, weight: 7000, diet: 'carnivore' }
10
11// Checking if a key exists in the map
12console.log(dinoStats.has('Velociraptor')); // true
13console.log(dinoStats.has('Brachiosaurus')); // false
14
15// Removing an element from the map
16dinoStats.delete('Triceratops');
17console.log(dinoStats.size); // 2
18
19// Clearing the entire map
20// dinoStats.clear();
21// console.log(dinoStats.size); // 0One of the main advantages of Map over plain objects is the ability to use keys of any type:
1// Map with different key types
2const dinoRecords = new Map();
3
4// Key: string
5dinoRecords.set('fastestRunner', 'Gallimimus');
6
7// Key: number
8dinoRecords.set(42, 'Number of dinosaurs in the park');
9
10// Key: object
11const paddock1 = { id: 'A1', name: 'T-Rex Kingdom' };
12dinoRecords.set(paddock1, ['Tyrannosaurus']);
13
14// Key: function
15function feedingTime() { return 'Time to feed the dinosaurs!'; }
16dinoRecords.set(feedingTime, '09:00 and 17:00');
17
18console.log(dinoRecords.get('fastestRunner')); // 'Gallimimus'
19console.log(dinoRecords.get(42)); // 'Number of dinosaurs in the park'
20console.log(dinoRecords.get(paddock1)); // ['Tyrannosaurus']
21console.log(dinoRecords.get(feedingTime)); // '09:00 and 17:00'1// Map with dinosaur data
2const dinosaurData = new Map([
3 ['Tyrannosaurus', { diet: 'carnivore', era: 'Late Cretaceous', danger: 5 }],
4 ['Velociraptor', { diet: 'carnivore', era: 'Late Cretaceous', danger: 4 }],
5 ['Triceratops', { diet: 'herbivore', era: 'Late Cretaceous', danger: 3 }],
6 ['Stegosaurus', { diet: 'herbivore', era: 'Late Jurassic', danger: 2 }],
7 ['Brachiosaurus', { diet: 'herbivore', era: 'Late Jurassic', danger: 1 }]
8]);
9
10// Iterating over key-value pairs
11console.log('Dinosaur information:');
12for (const [species, info] of dinosaurData) {
13 console.log(`${species} - ${info.diet}, danger level: ${info.danger}`);
14}
15
16// Iterating only over keys
17console.log('Dinosaur species:');
18for (const species of dinosaurData.keys()) {
19 console.log(`- ${species}`);
20}
21
22// Iterating only over values
23console.log('Danger levels:');
24for (const info of dinosaurData.values()) {
25 console.log(`Danger level: ${info.danger}, Diet: ${info.diet}`);
26}
27
28// Using forEach method
29dinosaurData.forEach((info, species) => {
30 console.log(`${species} from ${info.era} era, danger level: ${info.danger}`);
31});1// Converting Map to array of key-value pairs
2const entriesArray = [...dinosaurData];
3console.log('Entries array:', entriesArray);
4// [['Tyrannosaurus', {...}], ['Velociraptor', {...}], ...]
5
6// Converting Map to array of keys
7const speciesArray = [...dinosaurData.keys()];
8console.log('Array of species:', speciesArray);
9// ['Tyrannosaurus', 'Velociraptor', 'Triceratops', 'Stegosaurus', 'Brachiosaurus']
10
11// Converting Map to array of values
12const infoArray = [...dinosaurData.values()];
13console.log('Array of info:', infoArray);
14// [{ diet: 'carnivore', era: 'Late Cretaceous', danger: 5 }, ...]
15
16// Converting object to Map
17const dinoObject = {
18 Ankylosaurus: { weight: 5000, armor: 'high' },
19 Parasaurolophus: { weight: 2500, ability: 'vocalization' }
20};
21
22const dinoObjectMap = new Map(Object.entries(dinoObject));
23console.log('Map from object:', dinoObjectMap);
24
25// Converting Map to object
26const herbivores = new Map([
27 ['Triceratops', { horns: 3, frill: true }],
28 ['Stegosaurus', { backPlates: 17, tailSpikes: 4 }]
29]);
30
31const herbivoresObject = Object.fromEntries(herbivores);
32console.log('Object from map:', herbivoresObject);
33// { Triceratops: { horns: 3, frill: true }, Stegosaurus: { backPlates: 17, tailSpikes: 4 } }1// Map for managing dinosaur enclosures
2const enclosures = new Map();
3
4// Adding enclosure data
5enclosures.set('T-REX-01', {
6 dinosaurs: ['Tyrannosaurus'],
7 status: 'active',
8 securityLevel: 'maximum',
9 lastInspection: new Date('2023-06-10')
10});
11
12enclosures.set('RAP-02', {
13 dinosaurs: ['Velociraptor', 'Velociraptor', 'Velociraptor', 'Velociraptor'],
14 status: 'maintenance',
15 securityLevel: 'high',
16 lastInspection: new Date('2023-06-15')
17});
18
19enclosures.set('HERB-03', {
20 dinosaurs: ['Triceratops', 'Stegosaurus', 'Parasaurolophus'],
21 status: 'active',
22 securityLevel: 'medium',
23 lastInspection: new Date('2023-06-12')
24});
25
26// Function finding all active enclosures
27function getActiveEnclosures(enclosuresMap) {
28 const active = new Map();
29
30 for (const [id, data] of enclosuresMap) {
31 if (data.status === 'active') {
32 active.set(id, data);
33 }
34 }
35
36 return active;
37}
38
39const activeEnclosures = getActiveEnclosures(enclosures);
40console.log('Active enclosures:', [...activeEnclosures.keys()]);
41// ['T-REX-01', 'HERB-03']
42
43// Function finding enclosures needing inspection (last inspection older than 7 days)
44function findEnclosuresNeedingInspection(enclosuresMap) {
45 const today = new Date();
46 const sevenDaysAgo = new Date(today);
47 sevenDaysAgo.setDate(today.getDate() - 7);
48
49 return [...enclosuresMap].filter(([_, data]) =>
50 data.lastInspection < sevenDaysAgo
51 ).map(([id]) => id);
52}
53
54// Note: in a real example, dates would be more than a few days apart
55console.log('Enclosures needing inspection:', findEnclosuresNeedingInspection(enclosures));1// Map for tracking dinosaur statuses
2const dinosaurStatuses = new Map();
3
4// Adding and updating statuses
5function updateDinosaurStatus(id, status) {
6 const currentTime = new Date();
7
8 if (dinosaurStatuses.has(id)) {
9 const currentStatus = dinosaurStatuses.get(id);
10
11 // Keep status history
12 currentStatus.history.push({
13 status: currentStatus.current,
14 time: currentStatus.updatedAt
15 });
16
17 // Update status
18 currentStatus.current = status;
19 currentStatus.updatedAt = currentTime;
20
21 dinosaurStatuses.set(id, currentStatus);
22 } else {
23 // New entry for dinosaur
24 dinosaurStatuses.set(id, {
25 current: status,
26 updatedAt: currentTime,
27 history: []
28 });
29 }
30
31 return `Dinosaur ${id} status updated to: ${status}`;
32}
33
34// Using the function to track statuses
35console.log(updateDinosaurStatus('T-REX-001', 'feeding'));
36console.log(updateDinosaurStatus('V-RAP-004', 'resting'));
37
38// Later we update the status
39setTimeout(() => {
40 console.log(updateDinosaurStatus('T-REX-001', 'hunting'));
41 console.log(updateDinosaurStatus('V-RAP-004', 'active'));
42
43 // Displaying status history
44 console.log('T-REX-001 status history:', dinosaurStatuses.get('T-REX-001'));
45}, 1000);1// Map representing dinosaur kinship graph
2const dinoRelationships = new Map();
3
4// Adding dinosaurs and their relationships
5dinoRelationships.set('Blue', ['Delta', 'Echo', 'Charlie']); // Velociraptor and its siblings
6dinoRelationships.set('Delta', ['Blue', 'Echo', 'Charlie']);
7dinoRelationships.set('Echo', ['Blue', 'Delta', 'Charlie']);
8dinoRelationships.set('Charlie', ['Blue', 'Delta', 'Echo']);
9
10dinoRelationships.set('Rexy', ['Junior']); // T-Rex and its offspring
11dinoRelationships.set('Junior', ['Rexy']);
12
13// Function finding all related dinosaurs (network)
14function findRelatedDinosaurs(name, relationshipsMap) {
15 const visited = new Set();
16 const toVisit = [name];
17
18 while (toVisit.length > 0) {
19 const current = toVisit.pop();
20
21 if (!visited.has(current)) {
22 visited.add(current);
23
24 // Add related dinosaurs to visit
25 const related = relationshipsMap.get(current) || [];
26 toVisit.push(...related);
27 }
28 }
29
30 // Remove the original dinosaur from results
31 visited.delete(name);
32
33 return [...visited];
34}
35
36console.log('Dinosaurs related to Blue:', findRelatedDinosaurs('Blue', dinoRelationships));
37// ['Charlie', 'Echo', 'Delta']
38
39console.log('Dinosaurs related to Rexy:', findRelatedDinosaurs('Rexy', dinoRelationships));
40// ['Junior']| Feature | Map | Object | |---------|-----|--------| | Keys | Any type | Only strings and symbols | | Size |
.size property | Requires Object.keys(obj).length |
| Iteration | Built-in methods and interface | Requires additional methods |
| Order | Preserves insertion order | Implementation-dependent (undefined before ES2015) |
| Default keys | None | Inherits from prototype |
| Performance | Better for frequent add/remove | Better for simple cases |When to use Map:
When to use objects:
| Feature | Set | Array | |---------|-----|-------| | Unique values | Automatic duplicate removal | Can contain duplicates | | Element access | Only through iteration | Direct by index | | Operations | Add/remove/check in O(1) time | Presence check in O(n) time | | Order | Preserves insertion order | Indexed, preserves order | | Manipulation | Basic methods | Rich API with many methods |
When to use Set:
When to use arrays:
One of the main advantages of Map and Set is their performance. Operations such as adding, removing, and checking presence are performed in constant O(1) time, unlike array operations that may require O(n) time.
1// Performance comparison: array vs Set for checking presence
2
3// Creating large collections
4const largeArray = [];
5const largeSet = new Set();
6
7// Adding many elements
8for (let i = 0; i < 1000000; i++) {
9 largeArray.push(`item-${i}`);
10 largeSet.add(`item-${i}`);
11}
12
13// Measuring search time in array
14console.time('Array search');
15const arrayHasItem = largeArray.includes('item-500000');
16console.timeEnd('Array search');
17
18// Measuring search time in Set
19console.time('Set search');
20const setHasItem = largeSet.has('item-500000');
21console.timeEnd('Set search');
22
23console.log(`Array search result: ${arrayHasItem}`);
24console.log(`Set search result: ${setHasItem}`);
25
26// Results may vary, but Set should be significantly faster
27// Array search: 15ms
28// Set search: 0.01ms1// Map assigning abilities to dinosaurs
2const dinosaurAbilities = new Map();
3
4// Adding ability sets for different dinosaurs
5dinosaurAbilities.set('Tyrannosaurus', new Set([
6 'powerful bite', 'intimidating roar', 'keen sense of smell'
7]));
8
9dinosaurAbilities.set('Velociraptor', new Set([
10 'speed', 'pack hunting', 'intelligence', 'sharp claws'
11]));
12
13dinosaurAbilities.set('Triceratops', new Set([
14 'charging', 'defensive horns', 'thick skin'
15]));
16
17// Adding new abilities
18dinosaurAbilities.get('Tyrannosaurus').add('night vision');
19
20// Checking if a dinosaur has a specific ability
21function checkDinosaurAbility(species, ability) {
22 if (!dinosaurAbilities.has(species)) {
23 return `Unknown dinosaur species: ${species}`;
24 }
25
26 const abilities = dinosaurAbilities.get(species);
27 if (abilities.has(ability)) {
28 return `${species} has the ability: ${ability}`;
29 } else {
30 return `${species} does not have the ability: ${ability}`;
31 }
32}
33
34console.log(checkDinosaurAbility('Velociraptor', 'intelligence')); // Has it
35console.log(checkDinosaurAbility('Tyrannosaurus', 'flying')); // Does not have it
36console.log(checkDinosaurAbility('Brachiosaurus', 'height')); // Unknown species
37
38// Finding dinosaurs with a specific ability
39function findDinosaursWithAbility(ability) {
40 const result = [];
41
42 for (const [species, abilities] of dinosaurAbilities) {
43 if (abilities.has(ability)) {
44 result.push(species);
45 }
46 }
47
48 return result;
49}
50
51console.log('Dinosaurs with "intelligence" ability:',
52 findDinosaursWithAbility('intelligence')); // ['Velociraptor']1// Set of dinosaur reports (each report is a map)
2const dinoReports = new Set();
3
4// Creating several reports as Maps
5const report1 = new Map([
6 ['date', '2023-06-15'],
7 ['observer', 'Dr. Grant'],
8 ['species', 'Velociraptor'],
9 ['count', 3],
10 ['behavior', 'hunting'],
11 ['location', 'Sector B']
12]);
13
14const report2 = new Map([
15 ['date', '2023-06-16'],
16 ['observer', 'Dr. Sattler'],
17 ['species', 'Tyrannosaurus'],
18 ['count', 1],
19 ['behavior', 'feeding'],
20 ['location', 'Sector C']
21]);
22
23const report3 = new Map([
24 ['date', '2023-06-16'],
25 ['observer', 'Dr. Malcolm'],
26 ['species', 'Velociraptor'],
27 ['count', 2],
28 ['behavior', 'resting'],
29 ['location', 'Sector D']
30]);
31
32// Adding reports to the set
33dinoReports.add(report1);
34dinoReports.add(report2);
35dinoReports.add(report3);
36
37// Searching reports based on criteria
38function findReports(reports, criteria) {
39 return [...reports].filter(report => {
40 for (const [key, value] of Object.entries(criteria)) {
41 if (report.get(key) !== value) {
42 return false;
43 }
44 }
45 return true;
46 });
47}
48
49// Finding Velociraptor reports
50const raptorReports = findReports(dinoReports, { species: 'Velociraptor' });
51console.log('Velociraptor reports:', raptorReports.length);
52raptorReports.forEach(report => {
53 console.log(`${report.get('date')} - ${report.get('behavior')} - ${report.get('location')}`);
54});
55
56// Finding reports from a specific day
57const reportsFrom16 = findReports(dinoReports, { date: '2023-06-16' });
58console.log('Reports from June 16:', reportsFrom16.length);Map and Set are powerful data structures introduced in ES6 that enrich JavaScript's capabilities for organizing and manipulating data:
In the context of our Jurassic Park, these data structures allow us to:
Skillful use of these structures can significantly improve the performance and readability of code in JavaScript applications.