← Назад к вопросам

Реализуйте собственный 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;
};

Как это работает

Ключевые моменты:

  1. Цикл по элементам — проходим по каждому элементу массива
  2. Проверка i in this — обрабатываем sparse arrays правильно
  3. callback(value, index, array) — вызываем функцию с тремя аргументами
  4. Сохранение результатов — результаты добавляем в новый массив
  5. Возврат нового массива — исходный массив не изменяется

Что такое 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;
};