JavaScript Array.from(): Converting Iterables to Arrays
Master Array.from() for converting array-like objects and iterables to arrays. Learn mapping functions, creating ranges, and practical use cases.
Array.from() is a powerful static method that creates a new Array instance from an array-like or iterable object. Introduced in ES6, it provides a clean way to convert various data structures to arrays and apply transformations during conversion.
Understanding Array.from()
Array.from() creates a shallow copy of an array-like or iterable object, optionally transforming values as it goes.
Basic Syntax
// Syntax
Array.from(arrayLike);
Array.from(arrayLike, mapFn);
Array.from(arrayLike, mapFn, thisArg);
// Parameters:
// - arrayLike: An array-like or iterable object
// - mapFn (optional): Map function to call on every element
// - thisArg (optional): Value to use as 'this' when executing mapFn
// Basic example
const str = 'hello';
const chars = Array.from(str);
console.log(chars); // ['h', 'e', 'l', 'l', 'o']
// With mapping function
const numbers = Array.from('12345', (x) => Number(x));
console.log(numbers); // [1, 2, 3, 4, 5]
Converting Array-like Objects
Array-like objects have a length property and indexed elements but lack array methods.
NodeList and HTMLCollection
// Converting DOM NodeList
const divs = document.querySelectorAll('div');
const divArray = Array.from(divs);
// Now you can use array methods
divArray.forEach((div) => {
div.style.backgroundColor = 'lightblue';
});
// HTMLCollection
const forms = document.forms; // HTMLCollection
const formArray = Array.from(forms);
formArray
.filter((form) => form.checkValidity())
.forEach((form) => console.log('Valid form:', form.id));
// Getting text content from elements
const links = document.getElementsByTagName('a');
const linkTexts = Array.from(links, (link) => link.textContent);
console.log(linkTexts);
// Converting with index
const items = document.querySelectorAll('li');
const itemsWithIndex = Array.from(items, (item, index) => ({
text: item.textContent,
position: index,
}));
Arguments Object
// Converting arguments to array
function sum() {
// Old way: Array.prototype.slice.call(arguments)
const args = Array.from(arguments);
return args.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// With transformation
function doubleAndSum() {
const doubled = Array.from(arguments, (x) => x * 2);
return doubled.reduce((a, b) => a + b, 0);
}
console.log(doubleAndSum(1, 2, 3)); // 12 (2 + 4 + 6)
// Modern alternative with rest parameters
function modernSum(...args) {
return args.reduce((total, num) => total + num, 0);
}
Converting Iterables
Array.from() works with any iterable object, including built-in iterables and custom ones.
String Conversion
// Basic string to array
const word = 'JavaScript';
const letters = Array.from(word);
console.log(letters); // ['J', 'a', 'v', 'a', 'S', 'c', 'r', 'i', 'p', 't']
// Unicode handling
const emoji = '๐จโ๐ฉโ๐งโ๐ฆ๐';
const chars = Array.from(emoji);
console.log(chars); // ['๐จโ๐ฉโ๐งโ๐ฆ', '๐'] - Handles surrogate pairs correctly
// Character codes
const codes = Array.from('ABC', (char) => char.charCodeAt(0));
console.log(codes); // [65, 66, 67]
// Creating char ranges
const alphabet = Array.from({ length: 26 }, (_, i) =>
String.fromCharCode(65 + i)
);
console.log(alphabet); // ['A', 'B', 'C', ..., 'Z']
Set and Map Conversion
// Converting Set to Array
const uniqueNumbers = new Set([1, 2, 2, 3, 3, 4]);
const arrayFromSet = Array.from(uniqueNumbers);
console.log(arrayFromSet); // [1, 2, 3, 4]
// Transforming Set values
const doubled = Array.from(uniqueNumbers, (x) => x * 2);
console.log(doubled); // [2, 4, 6, 8]
// Converting Map to Array
const userMap = new Map([
['user1', { name: 'Alice' }],
['user2', { name: 'Bob' }],
['user3', { name: 'Charlie' }],
]);
// Array of entries
const entries = Array.from(userMap);
console.log(entries); // [['user1', {...}], ['user2', {...}], ...]
// Array of values
const users = Array.from(userMap.values());
console.log(users); // [{name: 'Alice'}, {name: 'Bob'}, ...]
// Array of keys
const userIds = Array.from(userMap.keys());
console.log(userIds); // ['user1', 'user2', 'user3']
// Transform during conversion
const userNames = Array.from(userMap, ([key, user]) => user.name);
console.log(userNames); // ['Alice', 'Bob', 'Charlie']
Custom Iterables
// Custom iterable object
const customIterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
const arrayFromCustom = Array.from(customIterable);
console.log(arrayFromCustom); // [1, 2, 3]
// Range generator
function* range(start, end, step = 1) {
for (let i = start; i <= end; i += step) {
yield i;
}
}
const numbers = Array.from(range(1, 10, 2));
console.log(numbers); // [1, 3, 5, 7, 9]
// Fibonacci generator
function* fibonacci(n) {
let [a, b] = [0, 1];
for (let i = 0; i < n; i++) {
yield a;
[a, b] = [b, a + b];
}
}
const fibArray = Array.from(fibonacci(10));
console.log(fibArray); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Creating Arrays with Array.from()
Array.from() can create arrays from objects with a length property.
Creating Sequences
// Creating array of specific length
const zeros = Array.from({ length: 5 });
console.log(zeros); // [undefined, undefined, undefined, undefined, undefined]
// Creating sequence of numbers
const sequence = Array.from({ length: 5 }, (_, i) => i);
console.log(sequence); // [0, 1, 2, 3, 4]
// Creating array with initial values
const fives = Array.from({ length: 5 }, () => 5);
console.log(fives); // [5, 5, 5, 5, 5]
// Creating array of objects
const users = Array.from({ length: 3 }, (_, i) => ({
id: i + 1,
name: `User ${i + 1}`,
active: true,
}));
console.log(users);
// [
// { id: 1, name: 'User 1', active: true },
// { id: 2, name: 'User 2', active: true },
// { id: 3, name: 'User 3', active: true }
// ]
Creating Ranges and Sequences
// Number range
function createRange(start, end) {
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
console.log(createRange(5, 10)); // [5, 6, 7, 8, 9, 10]
// Date range
function createDateRange(startDate, days) {
return Array.from({ length: days }, (_, i) => {
const date = new Date(startDate);
date.setDate(date.getDate() + i);
return date.toISOString().split('T')[0];
});
}
const dates = createDateRange('2024-01-01', 7);
console.log(dates);
// ['2024-01-01', '2024-01-02', ..., '2024-01-07']
// Creating multiplication table
const multiplicationTable = Array.from({ length: 10 }, (_, i) =>
Array.from({ length: 10 }, (_, j) => (i + 1) * (j + 1))
);
console.log(multiplicationTable[2][3]); // 12 (3 * 4)
Practical Use Cases
Data Transformation
// Parse CSV-like data
const csvData = '1,2,3\n4,5,6\n7,8,9';
const rows = csvData.split('\n');
const matrix = Array.from(rows, (row) => Array.from(row.split(','), Number));
console.log(matrix);
// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// Convert object to array with specific format
const scores = { math: 95, english: 87, science: 92 };
const scoreArray = Array.from(Object.entries(scores), ([subject, score]) => ({
subject,
score,
grade: score >= 90 ? 'A' : 'B',
}));
console.log(scoreArray);
// [
// { subject: 'math', score: 95, grade: 'A' },
// { subject: 'english', score: 87, grade: 'B' },
// { subject: 'science', score: 92, grade: 'A' }
// ]
// Normalize data
const rawData = [' John ', 'ALICE', 'bob'];
const normalized = Array.from(
rawData,
(name) =>
name.trim().charAt(0).toUpperCase() + name.trim().slice(1).toLowerCase()
);
console.log(normalized); // ['John', 'Alice', 'Bob']
Working with DOM
// Get all checked checkboxes
function getCheckedValues() {
const checkboxes = document.querySelectorAll(
'input[type="checkbox"]:checked'
);
return Array.from(checkboxes, (cb) => cb.value);
}
// Extract data attributes
const elements = document.querySelectorAll('[data-id]');
const ids = Array.from(elements, (el) => parseInt(el.dataset.id, 10));
// Create element array with computed styles
const paragraphs = document.querySelectorAll('p');
const paragraphData = Array.from(paragraphs, (p) => ({
text: p.textContent,
fontSize: window.getComputedStyle(p).fontSize,
color: window.getComputedStyle(p).color,
}));
// Filter and map in one go
const visibleDivs = Array.from(document.querySelectorAll('div'), (div) =>
div.offsetParent !== null ? div : null
).filter(Boolean);
Array Manipulation
// Deduplicate array
function deduplicate(array) {
return Array.from(new Set(array));
}
console.log(deduplicate([1, 2, 2, 3, 3, 4])); // [1, 2, 3, 4]
// Chunk array
function chunk(array, size) {
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
array.slice(i * size, (i + 1) * size)
);
}
console.log(chunk([1, 2, 3, 4, 5, 6, 7], 3));
// [[1, 2, 3], [4, 5, 6], [7]]
// Create array of arrays (matrix)
function createMatrix(rows, cols, initialValue = 0) {
return Array.from({ length: rows }, () =>
Array.from({ length: cols }, () => initialValue)
);
}
const matrix = createMatrix(3, 3, 0);
console.log(matrix);
// [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
// Zip arrays
function zip(...arrays) {
const maxLength = Math.max(...arrays.map((arr) => arr.length));
return Array.from({ length: maxLength }, (_, i) =>
arrays.map((arr) => arr[i])
);
}
console.log(zip([1, 2, 3], ['a', 'b', 'c'], [true, false, true]));
// [[1, 'a', true], [2, 'b', false], [3, 'c', true]]
Advanced Patterns
Functional Programming
// Pipe function results
const pipe =
(...fns) =>
(x) =>
Array.from(fns).reduce((v, f) => f(v), x);
const addOne = (x) => x + 1;
const double = (x) => x * 2;
const square = (x) => x * x;
const compute = pipe(addOne, double, square);
console.log(compute(3)); // 64 ((3 + 1) * 2)ยฒ
// Generate permutations
function* permutations(array) {
if (array.length <= 1) {
yield array;
return;
}
for (let i = 0; i < array.length; i++) {
const rest = [...array.slice(0, i), ...array.slice(i + 1)];
for (const perm of permutations(rest)) {
yield [array[i], ...perm];
}
}
}
const perms = Array.from(permutations([1, 2, 3]));
console.log(perms);
// [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
Memory-Efficient Patterns
// Lazy evaluation with generators
function* lazyMap(iterable, fn) {
for (const item of iterable) {
yield fn(item);
}
}
// Only processes what's needed
const hugeArray = Array.from({ length: 1000000 }, (_, i) => i);
const mapped = lazyMap(hugeArray, (x) => x * 2);
// Convert only first 10
const firstTen = Array.from({ length: 10 }, () => mapped.next().value);
console.log(firstTen); // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
// Pagination helper
function paginate(array, pageSize) {
const pageCount = Math.ceil(array.length / pageSize);
return {
pageCount,
getPage(pageNumber) {
const start = pageNumber * pageSize;
return Array.from(
{ length: Math.min(pageSize, array.length - start) },
(_, i) => array[start + i]
);
},
};
}
const data = Array.from({ length: 100 }, (_, i) => `Item ${i + 1}`);
const paginated = paginate(data, 10);
console.log(paginated.getPage(0)); // First 10 items
Performance Considerations
Array.from() vs Alternatives
// Performance comparison approaches
const nodeList = document.querySelectorAll('div');
// Array.from()
const arr1 = Array.from(nodeList);
// Spread operator
const arr2 = [...nodeList];
// Traditional loop
const arr3 = [];
for (let i = 0; i < nodeList.length; i++) {
arr3.push(nodeList[i]);
}
// Array.prototype.slice.call
const arr4 = Array.prototype.slice.call(nodeList);
// When to use each:
// - Array.from(): When you need the mapping function
// - Spread: Simple conversion, modern syntax
// - Loop: Maximum performance for large datasets
// - slice.call: Legacy code compatibility
Optimizing Large Datasets
// Batch processing large datasets
function* batchProcess(data, batchSize = 1000) {
for (let i = 0; i < data.length; i += batchSize) {
yield Array.from(
{ length: Math.min(batchSize, data.length - i) },
(_, j) => data[i + j]
);
}
}
// Process in chunks to avoid blocking
async function processLargeDataset(data, processor) {
for (const batch of batchProcess(data)) {
await processor(batch);
// Allow UI updates
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
Common Pitfalls and Solutions
// Pitfall 1: Sparse arrays
const sparse = [1, , , 4];
const fromSparse = Array.from(sparse);
console.log(fromSparse); // [1, undefined, undefined, 4]
// Pitfall 2: Non-iterable objects
const obj = { 0: 'a', 1: 'b', 2: 'c' };
// const arr = Array.from(obj); // Error: object is not iterable
// Solution: Add length property
const objWithLength = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const arr = Array.from(objWithLength);
console.log(arr); // ['a', 'b', 'c']
// Pitfall 3: this context in mapFn
const mapper = {
multiplier: 2,
multiply(x) {
return x * this.multiplier;
},
};
// Wrong: loses context
// const wrong = Array.from([1, 2, 3], mapper.multiply);
// Correct: bind context
const correct = Array.from([1, 2, 3], mapper.multiply, mapper);
console.log(correct); // [2, 4, 6]
Best Practices
- Use Array.from() when you need transformation
// Good: Transform while converting
const upperCaseLetters = Array.from('hello', (char) => char.toUpperCase());
// Less efficient: Convert then transform
const lessEfficient = [...'hello'].map((char) => char.toUpperCase());
- Prefer spread operator for simple conversions
// Simple conversion: use spread
const simple = [...nodeList];
// With transformation: use Array.from()
const transformed = Array.from(nodeList, (node) => node.textContent);
- Use descriptive variable names with index
// Clear intent
const numbered = Array.from({ length: 5 }, (_, index) => `Item ${index + 1}`);
// Even clearer with named parameters
const grid = Array.from({ length: 10 }, (unused, row) =>
Array.from({ length: 10 }, (unused, col) => `${row},${col}`)
);
Conclusion
Array.from() is a versatile method that simplifies converting various data structures to arrays while providing the ability to transform elements during conversion. It's particularly useful for working with DOM collections, creating sequences, and handling iterables. Understanding when to use Array.from() versus alternatives like the spread operator helps write more efficient and readable code. The method's ability to accept a mapping function makes it a powerful tool for data transformation tasks in modern JavaScript applications.