← Назад к вопросам
Реализуйте собственный map для массива
1.2 Junior🔥 181 комментариев
#Node.js и JavaScript#ООП
Условие
Реализуйте метод Array.prototype.myMap, который работает аналогично встроенному map:
Array.prototype.myMap = function(callback) {
// Ваш код
};
// Пример использования:
const arr = [1, 2, 3];
const doubled = arr.myMap(x => x * 2);
console.log(doubled); // [2, 4, 6]
// Должен работать с индексом и массивом:
const indexed = arr.myMap((val, idx, array) => `${idx}: ${val}`);
console.log(indexed); // ["0: 1", "1: 2", "2: 3"]
Что проверяется
- Понимание работы с прототипами
- Реализация встроенных методов массивов
- Работа с callback-функциями и контекстом this
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Базовая реализация
Array.prototype.myMap = function<T, U>(
callback: (value: T, index: number, array: T[]) => U
): U[] {
const result: U[] = [];
for (let i = 0; i < this.length; i++) {
// Проверяем, что индекс существует (для sparse arrays)
if (i in this) {
result[i] = callback(this[i], i, this);
}
}
return result;
};
Версия с поддержкой thisArg
Array.prototype.myMap = function<T, U>(
callback: (this: any, value: T, index: number, array: T[]) => U,
thisArg?: any
): U[] {
const result: U[] = [];
for (let i = 0; i < this.length; i++) {
if (i in this) {
// Вызываем callback с заданным контекстом this
result[i] = callback.call(thisArg, this[i], i, this);
}
}
return result;
};
Как это работает
Ключевые моменты:
- Цикл по элементам — проходим по каждому элементу массива
- Проверка
i in this— обрабатываем sparse arrays правильно - callback(value, index, array) — вызываем функцию с тремя аргументами
- Сохранение результатов — результаты добавляем в новый массив
- Возврат нового массива — исходный массив не изменяется
Что такое sparse array?
const sparse = [1, , 3]; // [1, <empty>, 3]
// Встроенный map пропускает пустые ячейки:
sparse.map(x => x * 2); // [2, <empty>, 6]
// Проверка `i in this` гарантирует то же поведение
Примеры использования
Пример 1: Простое преобразование
const arr = [1, 2, 3];
const doubled = arr.myMap(x => x * 2);
console.log(doubled); // [2, 4, 6]
Пример 2: С индексом и массивом
const arr = ['a', 'b', 'c'];
const indexed = arr.myMap((val, idx, array) => `${idx}: ${val}`);
console.log(indexed); // ["0: a", "1: b", "2: c"]
Пример 3: С контекстом this
const obj = {
multiplier: 10,
transform: function(x: number) {
return x * this.multiplier;
}
};
const arr = [1, 2, 3];
const result = arr.myMap(obj.transform, obj);
console.log(result); // [10, 20, 30]
Пример 4: Преобразование объектов
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const names = users.myMap(user => user.name);
console.log(names); // ["Alice", "Bob", "Charlie"]
Детальное объяснение
Порядок выполнения для [1, 2, 3].myMap(x => x * 2):
Шаг 1: i = 0
├─ 0 in [1, 2, 3] → true
├─ callback(1, 0, [1, 2, 3]) → 2
└─ result[0] = 2
Шаг 2: i = 1
├─ 1 in [1, 2, 3] → true
├─ callback(2, 1, [1, 2, 3]) → 4
└─ result[1] = 4
Шаг 3: i = 2
├─ 2 in [1, 2, 3] → true
├─ callback(3, 2, [1, 2, 3]) → 6
└─ result[2] = 6
Результат: [2, 4, 6]
Важные особенности
1. Sparse arrays (пропуски в массиве)
const sparse = [1, , 3]; // [1, <empty>, 3]
sparse.myMap(x => x * 2);
// [2, <empty>, 6]
// Индекс 1 не обрабатывается
2. Значение length
const arr = [1, 2, 3];
arr.length = 2;
arr.myMap(x => x * 2);
// [2, 4] — только первые 2 элемента
3. Изменения исходного массива
const arr = [1, 2, 3];
const result = arr.myMap((x) => {
arr[0] = 100; // Изменяем исходный массив
return x * 2;
});
console.log(result); // [2, 4, 6] — результат не зависит от изменений
Сравнение с встроенным map
// Оба работают одинаково:
const arr = [1, 2, 3];
arr.map(x => x * 2);
arr.myMap(x => x * 2);
// Результаты идентичны
Тестирование
import { describe, it, expect } from 'vitest';
describe('Array.prototype.myMap', () => {
it('должен применить функцию к каждому элементу', () => {
const arr = [1, 2, 3];
const result = arr.myMap(x => x * 2);
expect(result).toEqual([2, 4, 6]);
});
it('должен передавать индекс и массив', () => {
const arr = ['a', 'b', 'c'];
const result = arr.myMap((val, idx, array) => {
expect(typeof idx).toBe('number');
expect(array).toBe(arr);
return `${idx}: ${val}`;
});
expect(result).toEqual(['0: a', '1: b', '2: c']);
});
it('должен поддерживать контекст this', () => {
const context = { multiplier: 5 };
const arr = [1, 2, 3];
const result = arr.myMap(function(x: number) {
return x * (this as any).multiplier;
}, context);
expect(result).toEqual([5, 10, 15]);
});
it('должен обрабатывать sparse arrays', () => {
const sparse = [1, , 3] as Array<number | undefined>;
const result = sparse.myMap(x => x ? x * 2 : undefined);
expect(0 in result).toBe(true);
expect(1 in result).toBe(false);
expect(2 in result).toBe(true);
});
it('должен вернуть новый массив', () => {
const arr = [1, 2, 3];
const result = arr.myMap(x => x * 2);
expect(result).not.toBe(arr);
expect(arr).toEqual([1, 2, 3]); // Исходный не изменился
});
it('должен обрабатывать пустой массив', () => {
const arr: number[] = [];
const result = arr.myMap(x => x * 2);
expect(result).toEqual([]);
});
});
Другие методы для реализации
По аналогии можно реализовать:
// filter
Array.prototype.myFilter = function<T>(
callback: (value: T, index: number, array: T[]) => boolean,
thisArg?: any
): T[] {
const result: T[] = [];
for (let i = 0; i < this.length; i++) {
if (i in this && callback.call(thisArg, this[i], i, this)) {
result.push(this[i]);
}
}
return result;
};
// reduce
Array.prototype.myReduce = function<T, U>(
callback: (acc: U, val: T, idx: number, arr: T[]) => U,
initial?: U
): U {
let acc = initial as U;
let startIndex = 0;
if (initial === undefined) {
acc = this[0] as U;
startIndex = 1;
}
for (let i = startIndex; i < this.length; i++) {
if (i in this) {
acc = callback(acc, this[i], i, this);
}
}
return acc;
};