en.javascript.info/1-js/4-data-structures/8-array-methods/article.md
2015-01-11 01:54:57 +03:00

19 KiB
Raw Blame History

Массивы: методы

В этой главе мы рассмотрим встроенные методы массивов JavaScript. [cut]

Метод split

Ситуация из реальной жизни. Мы пишем сервис отсылки сообщений и посетитель вводит имена тех, кому его отправить: Маша, Петя, Марина, Василий.... Но нам-то гораздо удобнее работать с массивом имен, чем с одной строкой.

К счастью, есть метод split(s), который позволяет превратить строку в массив, разбив ее по разделителю s. В примере ниже таким разделителем является строка из запятой и пробела.

//+ run
var names = 'Маша, Петя, Марина, Василий';

var arr = names.split(', ');

for (var i=0; i<arr.length; i++) { 
  alert('Вам сообщение ' + arr[i]);
}

[smart header="Второй аргумент split"] У метода split есть необязательный второй аргумент -- ограничение на количество элементов в массиве. Если их больше, чем указано -- остаток массива будет отброшен:

//+ run
alert( "a,b,c,d".split(',', *!*2*/!*) ); // a,b

[/smart]

[smart header="Разбивка по буквам"] Вызов split с пустой строкой разобьёт по буквам:

//+ run
var str = "тест";

alert( str.split('') ); // т,е,с

[/smart]

Метод join

Вызов arr.join(str) делает в точности противоположное split. Он берет массив и склеивает его в строку, используя str как разделитель.

Например:

//+ run
var arr = ['Маша', 'Петя', 'Марина', 'Василий'];

var str = arr.join(';');

alert(str); // Маша;Петя;Марина;Василий

[smart header="new Array + join = Повторение строки"] Код для повторения строки 3 раза:

//+ run
alert( new Array(4).join("ля") ); // ляляля

Как видно, new Array(4) делает массив без элементов длины 4, который join объединяет в строку, вставляя между его элементами строку "ля".

В результате, так как элементы пусты, получается повторение строки. Такой вот небольшой трюк. [/smart]

Удаление из массива

Так как массивы являются объектами, то для удаления ключа можно воспользоваться обычным delete:

//+ run
var arr = ["Я", "иду", "домой"];

delete arr[1]; // значение с индексом 1 удалено

// теперь arr = ["Я", undefined, "домой"];
alert(arr[1]); // undefined

Да, элемент удален из массива, но не так, как нам этого хочется. Образовалась "дырка".

Это потому, что оператор delete удаляет пару "ключ-значение". Это -- все, что он делает. Обычно же при удалении из массива мы хотим, чтобы оставшиеся элементы сдвинулись и заполнили образовавшийся промежуток.

Поэтому для удаления используются специальные методы: из начала -- shift, с конца -- pop, а из середины -- splice, с которым мы сейчас познакомимся.

Метод splice

Метод splice -- это универсальный раскладной нож для работы с массивами. Умеет все: удалять элементы, вставлять элементы, заменять элементы -- по очереди и одновременно.

Его синтаксис:

`arr.splice(index[, deleteCount, elem1, ..., elemN])`
Удалить `deleteCount` элементов, начиная с номера `index`, а затем вставить `elem1, ..., elemN` на их место.

Посмотрим примеры.

//+ run
var arr = ["Я", "изучаю", "JavaScript"];

*!*
arr.splice(1, 1);  // начиная с позиции 1, удалить 1 элемент
*/!*

alert(arr); //  осталось ["Я", "JavaScript"]

Ниже продемонстрировано, как использовать splice для удаления одного элемента. Следующие за удаленным элементы сдвигаются, чтобы заполнить его место.

//+ run
var arr = ["Я", "изучаю", "JavaScript"];

*!*
arr.splice(0, 1);  // удалить 1 элемент, начиная с позиции 0
*/!*

alert( arr[0] ); // "изучаю" стал первым элементом

Следующий пример показывает, как заменять элементы:

//+ run
var arr = [*!*"Я", "сейчас", "изучаю",*/!* "JavaScript"];

// удалить 3 первых элемента и добавить другие вместо них
arr.splice(0, 3, "Мы", "изучаем") 

alert( arr ) // теперь [*!*"Мы", "изучаем"*/!*, "JavaScript"]

Метод splice возвращает массив из удаленных элементов:

//+ run
var arr = [*!*"Я", "сейчас",*/!* "изучаю", "JavaScript"];

// удалить 2 первых элемента 
var removed = arr.splice(0, 2); 

alert( removed ); // "Я", "сейчас" <-- array of removed elements

Метод splice также может вставлять элементы без удаления, для этого достаточно установить deleteCount в 0:

//+ run
var arr = ["Я", "изучаю", "JavaScript"];

// с позиции 2 
// удалить 0 
// вставить "сложный", "язык"
arr.splice(2, 0, "сложный", "язык"); 

alert(arr); // "Я", "изучаю", "сложный", "язык", "JavaScript"

Допускается использование отрицательного номера позиции, которая в этом случае отсчитывается с конца:

//+ run
var arr = [1, 2, 5]

// начиная с позиции индексом -1 (предпоследний элемент)
// удалить 0 элементов, 
// затем вставить числа 3 и 4
arr.splice(-1, 0, 3, 4);

alert(arr); // результат: 1,2,3,4,5

Метод slice

Метод slice(begin, end) копирует участок массива от begin до end, не включая end. Исходный массив при этом не меняется.

Например:

//+ run
var arr = ["Почему", "надо", "учить", "JavaScript"];

var arr2 = arr.slice(1,3); // элементы 1, 2 (не включая 3)

alert(arr2); // надо, учить

Аргументы ведут себя так же, как и в строковом slice:

  • Если не указать `end` -- копирование будет до конца массива:
    //+ run
    var arr = ["Почему", "надо", "учить", "JavaScript"];
    
    alert( arr.slice(1) ); // взять все элементы, начиная с номера 1
    
  • Можно использовать отрицательные индексы, они отсчитываются с конца:
    var arr2 = arr.slice(-2); // копировать от 2го элемента с конца и дальше
    
  • Если вообще не указать аргументов -- скопируется весь массив:
    var fullCopy = arr.slice();
    

[smart header="Совсем как в строках"] Синтаксис метода slice одинаков для строк и для массивов. Тем проще его запомнить. [/smart]

Сортировка, метод sort(fn)

Метод sort() сортирует массив на месте. Например:

//+ run
var arr = [ 1, 2, 15 ];

arr.sort();

alert( arr );  // *!*1, 15, 2*/!*

Не заметили ничего странного в этом примере?

Порядок стал 1, 15, 2, это точно не сортировка чисел. Почему?

Это произошло потому, что по умолчанию sort сортирует, преобразуя элементы к строке.

Поэтому и порядок у них строковый, ведь "2" > "15".

Свой порядок сортировки

Для указания своего порядка сортировки в метод arr.sort(fn) нужно передать функцию fn от двух элементов, которая умеет сравнивать их.

Внутренний алгоритм функции сортировки умеет сортировать любые массивы -- апельсинов, яблок, пользователей, и тех и других и третьих -- чего угодно. Но для этого ему нужно знать, как их сравнивать. Эту роль и выполняет fn.

Если эту функцию не указать, то элементы сортируются как строки.

Например, укажем эту функцию явно, отсортируем элементы массива как числа:

//+ run
function compareNumeric(a, b) {
  if (a > b) return 1;
  if (a < b) return -1;
}

var arr = [ 1, 2, 15 ];

*!*
arr.sort(compareNumeric);
*/!*

alert(arr);  // *!*1, 2, 15*/!*

Обратите внимание, мы передаём в sort() именно саму функцию compareNumeric, без вызова через скобки. Был бы ошибкой следующий код:

arr.sort( compareNumeric*!*()*/!* );  // не сработает

Как видно из примера выше, функция, передаваемая sort, должна иметь два аргумента.

Алгоритм сортировки, встроенный в JavaScript, будет передавать ей для сравнения элементы массива. Она должна возвращать:

  • Положительное значение, если `a > b`,
  • Отрицательное значение, если `a < b`,
  • Если равны -- можно `0`, но вообще -- не важно, что возвращать, их взаимный порядок не имеет значения.

[smart header="Алгоритм сортировки"] В методе sort, внутри самого интерпретатора JavaScript, реализован универсальный алгоритм сортировки. Как правило, это ""быстрая сортировка"", дополнительно оптимизированная для небольших массивов.

Он решает, какие пары элементов и когда сравнивать, чтобы отсортировать побыстрее. Мы даём ему функцию -- способ сравнения, дальше он вызывает её сам.

Кстати, те значения, с которыми sort вызывает функцию сравнения, можно увидеть, если вставить в неё alert:

//+ run
[1, -2, 15, 2, 0, 8].sort(function(a, b) {
  alert(a + " <> " + b);
});

[/smart]

[smart header="Сравнение compareNumeric в одну строку"] Функцию compareNumeric для сравнения элементов-чисел можно упростить до одной строчки.

function compareNumeric(a, b) {
  return a - b;
}

Эта функция вполне подходит для sort, так как возвращает положительное число, если a > b, отрицательное, если наоборот, и 0, если числа равны. [/smart]

reverse

Метод arr.reverse() меняет порядок элементов в массиве на обратный.

//+ run
var arr = [1,2,3];
arr.reverse();

alert(arr); // 3,2,1

concat

Метод arr.concat(value1, value2, ... valueN) создаёт новый массив, в который копируются элементы из arr, а также value1, value2, ... valueN.

Например:

//+ run
var arr = [1,2];
*!*
var newArr = arr.concat(3,4);
*/!*

alert(newArr); // 1,2,3,4

У concat есть одна забавная особенность.

Если аргумент concat -- массив, то concat добавляет элементы из него.

Например:

//+ run
var arr = [1,2];

*!*
var newArr = arr.concat( [3,4], 5);// то же самое, что arr.concat(3,4,5)
*/!*

alert(newArr); // 1,2,3,4,5

indexOf/lastIndexOf

Эти методы не поддерживаются в IE<9. Для их поддержки подключите библиотеку ES5-shim.

Метод "arr.indexOf(searchElement[, fromIndex])" возвращает номер элемента searchElement в массиве arr или -1, если его нет.

Поиск начинается с номера fromIndex, если он указан. Если нет -- с начала массива.

Для поиска используется строгое сравнение ===.

Например:

//+ run
var arr = [ 1, 0, false ];

alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.indexOf(null) ); // -1

Как вы могли заметить, по синтаксису он полностью аналогичен методу indexOf для строк.

Метод "arr.lastIndexOf(searchElement[, fromIndex])" ищет справа-налево: с конца массива или с номера fromIndex, если он указан.

[warn header="Методы indexOf/lastIndexOf осуществляют поиск перебором"] Если нужно проверить, существует ли значение в массиве -- его нужно перебрать. Только так. Внутренняя реализация indexOf/lastIndexOf осуществляет полный перебор, аналогичный циклу for по массиву. Чем длиннее массив, тем дольше он будет работать. [/warn]

[smart header="Коллекция уникальных элементов"] Рассмотрим задачу -- есть коллекция строк, и нужно быстро проверять: есть ли в ней какой-то элемент. Массив для этого не подходит из-за медленного indexOf. Но подходит объект! Доступ к свойству объекта осуществляется очень быстро, так что можно сделать все элементы ключами объекта и проверять, есть ли уже такой ключ.

Например, организуем такую проверку для коллекции строк "div", "a" и "form":

var store = { }; // объект для коллекции

var items = ["div", "a", "form"];

for(var i=0; i<items.length; i++) {
  var key = items[i]; // для каждого элемента создаём свойство
  store[ key ] = true; // значение здесь не важно
}

Теперь для проверки, есть ли ключ key, достаточно выполнить if (store[key]). Если есть -- можно использовать значение, если нет -- добавить.

Такое решение работает только со строками, но применимо к любым элементам, для которых можно вычислить строковый "уникальный ключ". [/smart]

Object.keys(obj)

Ранее мы говорили о том, что свойства объекта можно перебрать в цикле for..in.

Если мы хотим работать с ними в виде массива, то к нашим услугам -- замечательный метод Object.keys(obj). Он поддерживается везде, кроме IE8-:

//+ run
var user = {
  name: "Петя",
  age: 30
}

var keys = Object.keys(user);

alert(keys); // name, age

Итого

Методы:

  • `push/pop`, `shift/unshift`, `splice` -- для добавления и удаления элементов.
  • `join/split` -- для преобразования строки в массив и обратно.
  • `sort` -- для сортировки массива. Если не передать функцию сравнения -- сортирует элементы как строки.
  • `reverse` -- меняет порядок элементов на обратный.
  • `concat` -- объединяет массивы.
  • `indexOf/lastIndexOf` -- возвращают позицию элемента в массиве (не поддерживается в IE<9).

Изученных нами методов достаточно в 95% случаях, но существуют и другие. Для знакомства с ними рекомендуется заглянуть в справочник Array и Array в Mozilla Developer Network.