JS Drip #5 Изменение массива с Array#map

Опубликовано Пн. 26 Январь 2015 в Translations

Вольный перевод JS Drip#5 Transforming Arrays with Array#map

Одна из наиболее часто встречающихся задач в работе программиста на любом языке программирования - это создание и изменения массива значения. До недавнего времени в JavaScript для этого можно было использовать только шаблонный, приевшийся еще в Си, код.

Например так можно было затемнить цвет в формате RGB:

var colors = [
    {r: 255, g: 255, b: 255 }, // Белый
    {r: 128, g: 128, b: 128 }, // Серый
    {r: 0,   g: 0,   b: 0   }  // Черный
];

var newColors = [];

for (var i = 0; i < colors.length; i++) {
    transformed = {
        r: Math.round( colors[i].r / 2 ),
        g: Math.round( colors[i].g / 2 ),
        b: Math.round( colors[i].b / 2 )
    };

    newColors.push(transformed);
}

// Выведет:
// [
//    {r: 128, g: 128, b: 128 },
//    {r: 64,  g: 64,  b: 64  },
//    {r: 0,   g: 0,   b: 0   }
// ];
console.log(newColors);

Ты можешь заметить, что нетривиальная часть этого кода не так велика. Но, что бы эта часть заработала, нужно много второстепенных тривиальных кусков (перебор исходного массива,сохранение результата в новый). А что, если я скажу тебе, что на самом деле они не нужны?

Оказывается, в последней версии ECMAScript 5 (соответственно и последней версии JavaScript) у массивов есть классный метод map:

var newColors = colors.map(function(val) {
    return {
        r: Math.round( val.r / 2 ),
        g: Math.round( val.g / 2 ),
        b: Math.round( val.b / 2 )
    };
});

Вызов map возвращает новый массив, созданный последовательным вызовом функции для каждого элемента исходной функции. Т.е. ты можешь сконцентрироваться только на логике изменения исходного массива.

Конечно, map не ограничен только простейшими изменениями, вроде этого. Кроме текущего элемента твоя функция так же принимает еще два параметра: текущий индекс и исходный массив. Как-то так:

var starter = [1, 5, 5];

function multiplyByNext (val, index, arr) {
    var next = index + 1;

    // Если конец массива, то
    // используем первый элемент
    if (next === arr.length) {
        next = 0;
    }

    return val * arr[next];
}

var transformed = starter.map(multiplyByNext);

// Выведет: [5, 25, 5]
console.log(transformed);

Как видишь, с дополнительными параметрами функцию изменений можно сделать гораздо проще, если нужно обрабатывать соседние элементы. Например можно сделать что-то вроде этого: Игра "Жизнь".

Браузеры не плохо поддерживают map, но, увы не все. Как это часто бывает, она не поддерживается в IE8 и раньше. Есть несколько способов это обойти:

  1. Не использовать map.
  2. Использовать что-то вроде es5-shim что бы добавить поддержку map в IE.
  3. Использовать метод _.map из Underscore и Lodash как эквивалент. Одна из самых мощных техник предотвращения программных багов - это сокращение написанного кода. Метод массива map в этом плане отличный инструмент.
Яндекс.Метрика