Module 23: Array Advanced Methods
Advanced array methods provide powerful tools for data manipulation, transformation, and analysis in JavaScript.
1. Array.from()
1.1 Converting Iterables
// String to array
const str = 'hello';
const chars = Array.from(str);
console.log(chars); // ['h', 'e', 'l', 'l', 'o']
// Set to array
const set = new Set([1, 2, 3, 2, 1]);
const uniqueArray = Array.from(set);
console.log(uniqueArray); // [1, 2, 3]
// Map to array
const map = new Map([['a', 1], ['b', 2]]);
const entries = Array.from(map);
console.log(entries); // [['a', 1], ['b', 2]]
// NodeList to array
const divs = document.querySelectorAll('div');
const divsArray = Array.from(divs);
1.2 With Mapping Function
// Create range
const range = Array.from({ length: 5 }, (_, i) => i);
console.log(range); // [0, 1, 2, 3, 4]
// Create range with offset
const rangeFrom5 = Array.from({ length: 5 }, (_, i) => i + 5);
console.log(rangeFrom5); // [5, 6, 7, 8, 9]
// Generate squares
const squares = Array.from({ length: 5 }, (_, i) => i ** 2);
console.log(squares); // [0, 1, 4, 9, 16]
// Character codes
const str = 'abc';
const codes = Array.from(str, char => char.charCodeAt(0));
console.log(codes); // [97, 98, 99]
2. flatMap()
2.1 Basic flatMap
// Map and flatten
const arr = [1, 2, 3];
// Using map + flat
const mapped = arr.map(x => [x, x * 2]).flat();
console.log(mapped); // [1, 2, 2, 4, 3, 6]
// Using flatMap (more efficient)
const flatMapped = arr.flatMap(x => [x, x * 2]);
console.log(flatMapped); // [1, 2, 2, 4, 3, 6]
2.2 Practical Examples
// Split and flatten
const sentences = ['Hello world', 'How are you'];
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // ['Hello', 'world', 'How', 'are', 'you']
// Expand items
const orders = [
{ id: 1, items: ['apple', 'banana'] },
{ id: 2, items: ['orange'] }
];
const allItems = orders.flatMap(order => order.items);
console.log(allItems); // ['apple', 'banana', 'orange']
// Conditional expansion
const numbers = [1, 2, 3, 4];
const result = numbers.flatMap(n =>
n % 2 === 0 ? [n, n] : [n]
);
console.log(result); // [1, 2, 2, 3, 4, 4]
3. Advanced reduce()
3.1 Complex Aggregations
// Group by property
const people = [
{ name: 'John', age: 30, city: 'NYC' },
{ name: 'Jane', age: 25, city: 'LA' },
{ name: 'Bob', age: 35, city: 'NYC' }
];
const groupedByCity = people.reduce((acc, person) => {
const city = person.city;
acc[city] = acc[city] || [];
acc[city].push(person);
return acc;
}, {});
console.log(groupedByCity);
// { NYC: [John, Bob], LA: [Jane] }
3.2 Count Occurrences
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const counts = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(counts);
// { apple: 3, banana: 2, orange: 1 }
3.3 Index by Property
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 3, name: 'Bob' }
];
const usersById = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
console.log(usersById);
// { 1: {id: 1, name: 'John'}, 2: {...}, 3: {...} }
3.4 Nested Data Structures
// Array to nested object
const path = ['a', 'b', 'c'];
const value = 42;
const nested = path.reduceRight((acc, key) => ({ [key]: acc }), value);
console.log(nested);
// { a: { b: { c: 42 } } }
// Flatten nested arrays
const nested = [[1, 2], [3, [4, 5]], 6];
const flattened = nested.reduce((acc, item) => {
return acc.concat(Array.isArray(item) ? item.flat() : item);
}, []);
console.log(flattened); // [1, 2, 3, 4, 5, 6]
4. find() and findIndex()
4.1 find()
const users = [
{ id: 1, name: 'John', age: 30 },
{ id: 2, name: 'Jane', age: 25 },
{ id: 3, name: 'Bob', age: 35 }
];
// Find by property
const user = users.find(u => u.id === 2);
console.log(user); // { id: 2, name: 'Jane', age: 25 }
// Find by condition
const adult = users.find(u => u.age >= 30);
console.log(adult); // First user aged 30+
// Returns undefined if not found
const notFound = users.find(u => u.age > 100);
console.log(notFound); // undefined
4.2 findIndex()
const numbers = [10, 20, 30, 40, 50];
// Find index
const index = numbers.findIndex(n => n > 25);
console.log(index); // 2 (index of 30)
// Returns -1 if not found
const notFound = numbers.findIndex(n => n > 100);
console.log(notFound); // -1
// Remove found item
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 3, name: 'Bob' }
];
const indexToRemove = users.findIndex(u => u.id === 2);
if (indexToRemove !== -1) {
users.splice(indexToRemove, 1);
}
console.log(users); // [John, Bob]
4.3 findLast() and findLastIndex()
// Find from end (ES2023)
const numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
const lastEven = numbers.findLast(n => n % 2 === 0);
console.log(lastEven); // 2 (last even number)
const lastEvenIndex = numbers.findLastIndex(n => n % 2 === 0);
console.log(lastEvenIndex); // 7
5. some() and every()
5.1 some()
const numbers = [1, 2, 3, 4, 5];
// Check if any element matches
const hasEven = numbers.some(n => n % 2 === 0);
console.log(hasEven); // true
const hasNegative = numbers.some(n => n < 0);
console.log(hasNegative); // false
// Validation
const users = [
{ name: 'John', email: 'john@example.com' },
{ name: 'Jane', email: 'invalid' }
];
const hasInvalidEmail = users.some(u => !u.email.includes('@'));
console.log(hasInvalidEmail); // true
5.2 every()
const numbers = [2, 4, 6, 8, 10];
// Check if all elements match
const allEven = numbers.every(n => n % 2 === 0);
console.log(allEven); // true
const allPositive = numbers.every(n => n > 0);
console.log(allPositive); // true
// Validation
const users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 35 }
];
const allAdults = users.every(u => u.age >= 18);
console.log(allAdults); // true
6. Array Sorting
6.1 Basic Sorting
// Numbers (need compare function!)
const numbers = [10, 5, 8, 1, 7];
// ❌ Wrong (sorts as strings)
numbers.sort();
console.log(numbers); // [1, 10, 5, 7, 8]
// ✅ Correct
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 5, 7, 8, 10]
// Descending
numbers.sort((a, b) => b - a);
console.log(numbers); // [10, 8, 7, 5, 1]
6.2 Sorting Objects
const users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 35 }
];
// Sort by age
users.sort((a, b) => a.age - b.age);
console.log(users); // Jane(25), John(30), Bob(35)
// Sort by name
users.sort((a, b) => a.name.localeCompare(b.name));
console.log(users); // Bob, Jane, John
6.3 Multi-Level Sorting
const users = [
{ name: 'John', age: 30, city: 'NYC' },
{ name: 'Jane', age: 25, city: 'LA' },
{ name: 'Bob', age: 30, city: 'NYC' }
];
// Sort by age, then by name
users.sort((a, b) => {
if (a.age !== b.age) {
return a.age - b.age;
}
return a.name.localeCompare(b.name);
});
console.log(users); // Jane(25), Bob(30), John(30)
7. Array.prototype.at()
7.1 Positive and Negative Indexing
const arr = ['a', 'b', 'c', 'd', 'e'];
// Positive index
console.log(arr.at(0)); // 'a'
console.log(arr.at(2)); // 'c'
// Negative index (from end)
console.log(arr.at(-1)); // 'e' (last)
console.log(arr.at(-2)); // 'd' (second from end)
// Comparison with bracket notation
console.log(arr[arr.length - 1]); // Old way
console.log(arr.at(-1)); // New way (cleaner)
8. Array Grouping
8.1 Manual Grouping
const products = [
{ name: 'Laptop', category: 'Electronics', price: 1000 },
{ name: 'Mouse', category: 'Electronics', price: 25 },
{ name: 'Desk', category: 'Furniture', price: 300 }
];
// Group by category
const byCategory = products.reduce((acc, product) => {
const category = product.category;
acc[category] = acc[category] || [];
acc[category].push(product);
return acc;
}, {});
console.log(byCategory);
8.2 Using Object.groupBy() (ES2024)
// Future standard
const products = [
{ name: 'Laptop', category: 'Electronics', price: 1000 },
{ name: 'Mouse', category: 'Electronics', price: 25 },
{ name: 'Desk', category: 'Furniture', price: 300 }
];
const byCategory = Object.groupBy(products, product => product.category);
console.log(byCategory);
9. Chaining Methods
9.1 Complex Pipelines
const users = [
{ name: 'John', age: 30, active: true, score: 85 },
{ name: 'Jane', age: 25, active: false, score: 92 },
{ name: 'Bob', age: 35, active: true, score: 78 },
{ name: 'Alice', age: 28, active: true, score: 95 }
];
// Multi-step transformation
const result = users
.filter(user => user.active) // Active users only
.filter(user => user.score >= 80) // Score >= 80
.map(user => ({ // Transform
name: user.name,
grade: user.score >= 90 ? 'A' : 'B'
}))
.sort((a, b) => b.grade.localeCompare(a.grade)) // Sort by grade
.slice(0, 2); // Top 2
console.log(result);
// [{ name: 'Alice', grade: 'A' }, { name: 'John', grade: 'B' }]
9.2 Performance Considerations
// ✅ Efficient: single pass
const result = arr.reduce((acc, item) => {
if (item.active && item.score >= 80) {
acc.push({ name: item.name, grade: item.score >= 90 ? 'A' : 'B' });
}
return acc;
}, []);
// ❌ Less efficient: multiple passes
const result = arr
.filter(item => item.active)
.filter(item => item.score >= 80)
.map(item => ({ name: item.name, grade: item.score >= 90 ? 'A' : 'B' }));
10. Practical Patterns
10.1 Pagination
function paginate(array, page, pageSize) {
const start = (page - 1) * pageSize;
const end = start + pageSize;
return array.slice(start, end);
}
const items = Array.from({ length: 100 }, (_, i) => i + 1);
console.log(paginate(items, 1, 10)); // [1-10]
console.log(paginate(items, 2, 10)); // [11-20]
console.log(paginate(items, 3, 10)); // [21-30]
10.2 Chunking
function chunk(array, size) {
return Array.from(
{ length: Math.ceil(array.length / size) },
(_, i) => array.slice(i * size, i * size + size)
);
}
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(chunk(numbers, 3));
// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
10.3 Unique Values
// Using Set
const arr = [1, 2, 2, 3, 3, 3, 4, 5];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3, 4, 5]
// Unique objects by property
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 1, name: 'John' }
];
const uniqueUsers = Array.from(
new Map(users.map(user => [user.id, user])).values()
);
console.log(uniqueUsers); // [John, Jane]
10.4 Intersection and Difference
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];
// Intersection
const intersection = arr1.filter(x => arr2.includes(x));
console.log(intersection); // [3, 4, 5]
// Difference
const difference = arr1.filter(x => !arr2.includes(x));
console.log(difference); // [1, 2]
// Union
const union = [...new Set([...arr1, ...arr2])];
console.log(union); // [1, 2, 3, 4, 5, 6, 7]
10.5 Min/Max
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
// Simple min/max
const min = Math.min(...numbers);
const max = Math.max(...numbers);
console.log(min); // 1
console.log(max); // 9
// Min/max by property
const users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 35 }
];
const oldest = users.reduce((prev, current) =>
current.age > prev.age ? current : prev
);
console.log(oldest); // Bob (35)
10.6 Shuffle
function shuffle(array) {
const result = [...array];
for (let i = result.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[result[i], result[j]] = [result[j], result[i]];
}
return result;
}
const cards = ['A', 'K', 'Q', 'J'];
console.log(shuffle(cards)); // Random order
11. Best Practices
11.1 Immutability
// ❌ Mutates original
const arr = [1, 2, 3];
arr.push(4);
arr.sort();
// ✅ Returns new array
const arr = [1, 2, 3];
const newArr = [...arr, 4].sort((a, b) => a - b);
11.2 Avoid Nested Callbacks
// ❌ Hard to read
const result = arr
.map(x => x.items)
.flat()
.filter(item => item.active)
.map(item => item.id);
// ✅ Use flatMap
const result = arr
.flatMap(x => x.items)
.filter(item => item.active)
.map(item => item.id);
11.3 Prefer Specific Methods
// ❌ Less clear
const hasEven = arr.filter(n => n % 2 === 0).length > 0;
// ✅ More expressive
const hasEven = arr.some(n => n % 2 === 0);
Performance
For large arrays, consider:
- Using for loops instead of chaining for performance
- Breaking chains early with some()/every()
- Using memoization for expensive calculations
Summary
In this module, you learned:
- ✅ Array.from() for creating and transforming arrays
- ✅ flatMap() for mapping and flattening
- ✅ Advanced reduce() patterns
- ✅ find(), findIndex(), some(), every()
- ✅ Sorting arrays and objects
- ✅ Array.prototype.at() for negative indexing
- ✅ Grouping and aggregation
- ✅ Method chaining strategies
- ✅ Practical patterns: pagination, chunking, unique, etc.
Next Steps
In Module 24, you'll learn about Iterators and Generators for custom iteration.
Practice Exercises
- Create a pipeline to transform and analyze data
- Implement custom groupBy and partition functions
- Build a data aggregation system
- Create array utilities: chunk, unique, intersection, etc.
- Implement advanced sorting with multiple criteria
- Build a pagination system
- Create a search/filter system
- Implement performance-optimized transformations