Skip to main content

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

  1. Create a pipeline to transform and analyze data
  2. Implement custom groupBy and partition functions
  3. Build a data aggregation system
  4. Create array utilities: chunk, unique, intersection, etc.
  5. Implement advanced sorting with multiple criteria
  6. Build a pagination system
  7. Create a search/filter system
  8. Implement performance-optimized transformations

Additional Resources