PHP: Обход массивов. Все способы перебора массива в JavaScript Метод forEach и родственные методы

Массив в 1С — это набор некоторых значений. Значения в одном массиве могут быть различных типов.

Массив может быть получен в результате выполнения различных функций, например, ВыгрузитьКолонку() таблицы значений; в виде массива можно получить выделенные строки динамического списка и т.д. Можно создать массив и «вручную».

Создание массива

1. Как создать массив нужного размера

пМассив = новый Массив(4 ) ; //создали массив из 4 элементов

ПМассив[ 0 ] = "Мы" ;
пМассив[ 1 ] = "создали" ;
пМассив[ 2 ] = "новый" ;
пМассив[ 3 ] = "массив" ;

2. Как создать пустой массив и добавить в него элементы

пМассив = новый Массив; //создали пустой массив

//Внесем значения элементов массива

ПМассив. Добавить("Мы" ) ;


3. Как создать многомерный массив.

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

пМассив = новый Массив (4 , 2 ) ; //создали массив 4х2

//Внесем значения элементов массива, пронумеровав каждое слово

ПМассив[ 0 ] [ 0 ] = "1. " ;
пМассив[ 0 ] [ 1 ] = "Мы" ;
пМассив[ 1 ] [ 0 ] = "2. " ;
пМассив[ 1 ] [ 1 ] = "создали" ;
пМассив[ 2 ] [ 0 ] = "3. " ;
пМассив[ 2 ] [ 1 ] = "многомерный" ;
пМассив[ 3 ] [ 0 ] = "4. " ;
пМассив[ 3 ] [ 1 ] = "массив" ;

4. Как создать фиксированный массив

Фиксированный массив отличается от обычного тем, что его нельзя изменять. Нельзя добавлять, удалять или менять значения элементов такого массива.

Фиксированный массив может быть получен из обычного:

пМассив = новый Массив;

ПМассив. Добавить("Мы" ) ;
пМассив. Добавить("создали" ) ;
пМассив. Добавить("новый" ) ;
пМассив. Добавить("массив" ) ;

ФМассив = новый ФиксированныйМассив(пМассив) ; // создали фиксированный массив

Функции работы с массивами

Работу функций будем рассматривать на примере одномерного массива пМассив , созданного выше и состоящего из 4 элементов:

  1. «создали»
  2. «новый»
  3. «массив».
Функция ВГраница()

Получает наибольший индекс элемента массива. Он всегда на единицу меньше количества элементов массива.

Индекс = пМассив. ВГраница() // 3;

Функция Вставить()

Вставляет какое-то значение в элемент массива с указанным индексом. Последующие элементы массива сдвигаются

пМассив. Вставить(3 , "новое значение" ) //Теперь массив состоит из 5 элементов

Функция Добавить()

Создает новый элемент в конце массива и вставляет туда заданное значение

пМассив. Добавить("." ) // добавили точку пятым элементов массива;

Функция Количество()

Возвращает количество элементов массива.

пМассив. Количество() ; // 4

Функция Найти()

Ищет в массиве заданный элемент. Если находит, возвращает его индекс. Если не находит, возвращает Неопределено .

Индекс = пМассив. Найти("массив" ) ; // 3
Индекс = пМассив. Найти("строка, которой не было" ) ; // Неопределено

Функция Очистить()

Удаляет все значения из массива.

пМассив. Очистить() ;

Функция Получить()

Получает значение массива по индексу. Эту же задачу можно решить через .

Значение= пМассив. Получить(3 ) // "массив"
Значение= пМассив[ 3 ] ; // "массив"

Функция Удалить()

Удаляет элемент массива по индексу

пМассив. Удалить(3 ) ;

Функция Установить()

Устанавливает значение элемента массива по индексу. Работает аналогично .

пМассив. Установить(3 , "массив!" ) ;
пМассив[ 3 ] = "массив!" ;

Как обойти массив

Можно обойти все элементы массива без указания индекса:

Для каждого ЭлементМассива из пМассив Цикл
Сообщить(ЭлементМассива) ;
КонецЦикла ;

Можно при обходе использовать индекс:

Для Индекс= 0 по пМассив. ВГраница() Цикл
Сообщить(пМассив[ Индекс] ) ;
КонецЦикла ;

Как обойти многомерный массив

Многомерный массив обходится с помощью тех же циклов (см. выше), но один цикл должен быть вложен в другой.

Для каждого Элемент1 из мМассив Цикл
Для каждого Элемент2 из Элемент1 Цикл
Сообщить(Элемент1 ) ;
КонецЦикла ;
КонецЦикла ;

Или с применением индексов.

мМассив= новый массив(3 , 4 ) ;

Для Индекс1 = 0 по мМассив. ВГраница() Цикл
Для Индекс2 по мМассив[ Индекс1 ] . ВГраница() Цикл
Сообщить(мМассив[ Индекс1 ] [ Индекс2 ] ) ;
КонецЦикла ;
КонецЦикла ;

Сортировка массива

Для сортировки массива нам потребуется вспомогательный объект с типом СписокЗначений.

СписокЗнач = новый СписокЗначений; // создаем список значений
СписокЗнач. ЗагрузитьЗначения(пМассив) ; // загружаем в список значения из массива
СписокЗнач. СортироватьПоЗначению(НаправлениеСортировки. Возр) ; //сортируем по возрастанию
СписокЗнач. СортироватьПоЗначению(НаправлениеСортировки. Убыв) ; //или по убыванию
пМассив= СписокЗнач. Выгрузить() ; // выгружаем отсортированные значения обратно в массив

Сравнение двух массивов

Перед тем, как перейти к описанию функции сравнения, договоримся, что массивы считаются идентичными, если имеют одинаковое количество элементов и соответствующие элементы массивов равны. Тогда для сравнения можно использовать следующую функцию (кстати, такая функция уже присутствует в некоторых типовых конфигурациях):

Функция СравнитьМассивы(Массив1 , Массив2 )

Если Массив1 . Количество() <> Массив2 . Количество() Тогда
Возврат ЛОЖЬ; // Массивы не равны, сравнивать элементы нет смысла.
КонецЕсли ;

Для Индекс= 0 по Массив1 . ВГраница() Цикл
Если Массив1 [ Индекс] <> Массив2 [ Индекс] Тогда
Возврат Ложь ; //эти элементы не равны, значит и массивы не равны
КонецЕсли ;
КонецЦикла ;

Возврат Истина ; // Если дошли до сюда, то массивы равны
КонецФункции

В функцию нужно передать 2 сравниваемых массива. Функция возвращает значение Истина , если массивы равны, и Ложь , если не равны.

  • I. Перебор настоящих массивов
    1. Метод forEach и родственные методы
    2. Цикл for
    3. Правильное использование цикла for...in
    4. Цикл for...of (неявное использование итератора)
    5. Явное использование итератора
    1. Использование способов перебора настоящих массивов
    2. Преобразование в настоящий массив
    3. Замечание по объектам среды исполнения

I. Перебор настоящих массивов

На данный момент есть три способа перебора элементов настоящего массива:
  1. метод Array.prototype.forEach ;
  2. классический цикл for ;
  3. «правильно» построенный цикл for...in .
Кроме того, в скором времени, с появлением нового стандарта ECMAScript 6 (ES 6), ожидается еще два способа:
  1. цикл for...of (неявное использование итератора);
  2. явное использование итератора.

1. Метод forEach и родственные методы

Если ваш проект рассчитан на поддержку возможностей стандарта ECMAScript 5 (ES5), вы можете использовать одно из его нововведений - метод forEach .

Пример использования:
var a = ["a", "b", "c"]; a.forEach(function(entry) { console.log(entry); });
В общем случае использование forEach требует подключения библиотеки эмуляции es5-shim для браузеров, не имеющих нативной поддержки этого метода. К ним относятся IE 8 и более ранние версии, которые до сих пор кое-где еще используются.

К достоинствам forEach относится то, что здесь не нужно объявлять локальные переменные для хранения индекса и значения текущего элемента массива, поскольку они автоматически передаются в функцию обратного вызова (колбек) в качестве аргументов.

Если вас беспокоят возможные затраты на вызов колбека для каждого элемента, не волнуйтесь и прочитайте это .

ForEach предназначен для перебора всех элементов массива, но кроме него ES5 предлагает еще несколько полезных методов для перебора всех или некоторых элементов плюс выполнения при этом каких-либо действий с ними:

  • every - возвращает true , если для каждого элемента массива колбек возвращает значение приводимое к true .
  • some - возвращает true , если хотя бы для одного элемента массива колбек возвращает значение приводимое к true .
  • filter - создает новый массив, включающий те элементы исходного массива, для которых колбек возвращает true .
  • map - создает новый массив, состоящий из значений возращаемых колбеком.
  • reduce - сводит массив к единственному значению, применяя колбек по очереди к каждому элементу массива, начиная с первого (может быть полезен для вычисления суммы элементов массива и других итоговых функций).
  • reduceRight - работает аналогично reduce, но перебирает элементы в обратном порядке.

2. Цикл for

Старый добрый for рулит :

Var a = ["a", "b", "c"]; var index; for (index = 0; index < a.length; ++index) { console.log(a); }
Если длина массива неизменна в течение всего цикла, а сам цикл принадлежит критическому в плане производительности участку кода (что маловероятно), то можно использовать «более оптимальную» версию for с хранением длины массива:

Var a = ["a", "b", "c"]; var index, len; for (index = 0, len = a.length; index < len; ++index) { console.log(a); }
Теоретически этот код должен выполняться чуть быстрее, чем предыдущий.

Если порядок перебора элементов не важен, то можно пойти еще дальше в плане оптимизации и избавиться от переменной для хранения длины массива, изменив порядок перебора на обратный:

Var a = ["a", "b", "c"]; var index; for (index = a.length - 1; index >= 0; --index) { console.log(a); }
Тем не менее, в современных движках JavaScript подобные игры с оптимизацией обычно ничего не значат.

3. Правильное использование цикла for...in

Если вам посоветуют использовать цикл for...in , помните, что перебор массивов - не то, для чего он предназначен . Вопреки распространенному заблуждению цикл for...in перебирает не индексы массива, а перечислимые свойства объекта.

Тем не менее, в некоторых случаях, таких как перебор разреженных массивов , for...in может оказаться полезным, если только соблюдать при этом меры предосторожности, как показано в примере ниже:

// a - разреженный массив var a = ; a = "a"; a = "b"; a = "c"; for (var key in a) { if (a.hasOwnProperty(key) && /^0$|^\d*$/.test(key) && key <= 4294967294) { console.log(a); } }
В данном примере на каждой итерации цикла выполняется две проверки:

  1. то, что массив имеет собственное свойство с именем key (не наследованное из его прототипа).
  2. то, что key - строка, содержащая десятичную запись целого числа, значение которого меньше 4294967294 . Откуда берется последнее число? Из определения индекса массива в ES5, из которого следует, что наибольший индекс, который может иметь элемент в массиве: (2^32 - 2) = 4294967294 .
Конечно, такие проверки отнимут лишнее время при выполнении цикла. Но в случае разреженного массива этот способ более эффективен, чем цикл for , поскольку в этом случае перебираются только те элементы, которые явно определены в массиве. Так, в примере выше будет выполнено всего 3 итерации (для индексов 0, 10 и 10000) - против 10001 в цикле for .

Чтобы не писать такой громоздкий код проверок каждый раз, когда требуется перебор массива, можно оформить его в виде отдельной функции:

Function arrayHasOwnIndex(array, key) { return array.hasOwnProperty(key) && /^0$|^\d*$/.test(key) && key <= 4294967294; }
Тогда тело цикла из примера значительно сократится:

For (key in a) { if (arrayHasOwnIndex(a, key)) { console.log(a); } }
Рассмотренный выше код проверок является универсальным, подходящим для всех случаев. Но вместо него можно использовать более короткую версию, хотя формально и не совсем правильную, но, тем не менее, подходящую для большинства случаев:

For (key in a) { if (a.hasOwnProperty(key) && String(parseInt(key, 10)) === key) { console.log(a); } }

4. Цикл for...of (неявное использование итератора)

ES6, пока все еще пребывающий в статусе черновика , должен ввести в JavaScript итераторы.

Итератор - это реализуемый объектом протокол, который определяет стандартный способ получения последовательности значений (конечной или бесконечной).
Итератор - это объект, в котором определен метод next() - функция без аргументов, возвращающая объект с двумя свойствами:

  1. done (boolean) - принимает значение true , если итератор достиг конца итерируемой последовательности. В противном случае имеет значение false .
  2. value - определяет значение, возвращаемое итератором. Может быть не определено (отсутствовать), если свойство done имеет значение true .
Многие встроенные объекты, в т.ч. настоящие массивы, имеют итераторы по умолчанию. Простейший способ применения итератора в настоящих массивах - использовать новую конструкцию for...of .

Пример использования for...of:

Var val; var a = ["a", "b", "c"]; for (val of a) { console.log(val); }
В приведенном примере цикл for...of неявно вызывает итератор объекта Array для получения каждого значения массива.

5. Явное использование итератора

Итераторы можно также использовать и явно, правда, в этом случае код становится значительно сложнее, по сравнению с циклом for...of . Выглядит это примерно так:

Var a = ["a", "b", "c"]; var it = a.entries(); var entry; while (!(entry = it.next()).done) { console.log(entry.value); }
В данном примере метод Array.prototype.entries возвращает итератор, который используется для вывода значений массива. На каждой итерации entry.value содержит массив вида [ключ, значение] .

II. Перебор массивоподобных объектов

Кроме настоящих массивов, в JavaScript встречаются также массивоподобные объекты . С настоящими массивами их роднит то, что они имеют свойство length и свойства с именами в виде чисел, соответствующие элементам массива. В качестве примеров можно назвать DOM коллекции NodeList и псевдомассив arguments , доступный внутри любой функции/метода.

1. Использование способов перебора настоящих массивов

Как минимум большинство, если не все, способы перебора настоящих массивов могут быть применены для перебора массивоподобных объектов.

Конструкции for и for...in могут быть применены к массивоподобным объектам точно тем же путем, что и к настоящим массивам.

ForEach и другие методы Array.prototype также применимы к массивоподобным объектам. Для этого нужно использовать вызов Function.call или Function.apply .

Например, если вы хотите применить forEach к свойству childNodes объекта Node , то это делается так:

Array.prototype.forEach.call(node.childNodes, function(child) { // делаем что-нибудь с объектом child });
Для удобства повторного использования этого приема, можно объявить ссылку на метод Array.prototype.forEach в отдельной переменной и использовать ее как сокращение:

// (Предполагается, что весь код ниже находится в одной области видимости) var forEach = Array.prototype.forEach; // ... forEach.call(node.childNodes, function(child) { // делаем что-нибудь с объектом child });
Если в массивоподобном объекте имеется итератор, то его можно использовать явно или неявно для перебора объекта таким же способом, как и для настоящих массивов.

2. Преобразование в настоящий массив

Есть также еще один, очень простой, способ перебора массивоподобного объекта: преобразовать его в настоящий массив и использовать любой из рассмотренных выше способов перебора настоящих массивов. Для преобразования можно использовать универсальный метод Array.prototype.slice , который может быть применен к любому массивоподобному объекту. Делается это очень просто, как показано в примере ниже:

Var trueArray = Array.prototype.slice.call(arrayLikeObject, 0);
Например, если вы хотите преобразовать коллекцию NodeList в настоящий массив, вам нужен примерно такой код:

Var divs = Array.prototype.slice.call(document.querySelectorAll("div"), 0);
Update : Как было отмечено в комментариях rock и torbasow , в ES6 вместо Array.prototype.slice можно использовать более наглядный метод Array.from .

3. Замечание по объектам среды исполнения

Если вы применяете методы Array.prototype к объектам среды исполнения (таких как DOM коллекции), то вы должны иметь в виду, что правильная работа этих методов не гарантирована во всех средах исполнения (в т.ч. в браузерах). Это зависит от поведения конкретного объекта в конкретной среде исполнения, если точнее, от того, как в этом объекте реализована абстрактная операция HasProperty . Проблема в том, что сам стандарт ES5 допускает возможность неправильного поведения объекта по отношению к этой операции (см. §8.6.2).

Поэтому важно тестировать работу методов Array.prototype в каждой среде исполнения (браузере), в которой планируется использование вашего приложения.

Функция print_r() позволяет посмотреть на весь массив целиком, но не может выделить отдельные его части. Представьте себе задачу, когда требуется вывести из массива элементы, удовлетворяющие определенному условию. В этом случае функция print_r() не сможет нам помочь. Для решения подобных задач применяют операторы цикла. Но и здесь нас ожидают подводные камни. Сначала разберем самый простой случай, когда массив в качестве ключей имеет последовательный ряд чисел.

Воспользуемся оператором цикла for , так как его запись является очень компактной и практичной. Единственное, что нам надо выяснить, – это количество элементов в массиве. На практике очень часто для решения данной проблемы применяют функцию count(). В качестве входного параметра для нее является массив, а возвращает она количество элементов в нем.

Обход массива

Итак, предположим, что ключ первого элемента в нашем массиве будет 0. Таким образом, мы проходим по всем элементам массива и выводим их. В этот цикл уже можно добавить определенное условие, предъявляемое к элементам массива.

У вас может возникнуть вопрос о присутствии переменной $len_mass в программе, так как функцию count() можно компактно уместить во второе выражение цикла for. Сразу хочется сказать, что делать этого не следует, потому что при этом возникают два негативных момента. Во-первых, функция count() будет выполняться при каждой итерации, что увеличивает время обработки, а во-вторых, количество элементов массива может поменяться в теле цикла.

К сожалению а может и к счастью, рассматриваемый массив является лишь частным случаем. На практике обычно встречаются массивы с непоследовательной индексацией. Да и применение цикла for сегодня является неактуальным, так как есть специальная конструкция foreach, которая появилась только в четвертой версии РНР. В общем случае она выглядит так, как показано ниже.

Конструкция foreach

1 2 3 4 foreach ($massiv as $key => $value) { // действия }

В этой конструкции мы видим три переменные, среди которых $massiv – это просматриваемый массив. Переменные $кеу и $value (названия этих переменных вы выбираете сами) содержат соответственно ключ и значение. Итак, поясним ход работы этого оператора цикла на примере:

Обход массива с помощью конструкции foreach

Итак, в первой строчке мы создаем массив из трех элементов. Заметьте, что индексация производится с помощью строк. Далее следует конструкция, которая начинается со слова foreach. Затем в круглых скобках помещается рассматриваемый массив, после которого записывается специальное слово as. После него следует пара ключ-значение, разделяемая оператором =>.

Итак, на первой итерации переменной $kеу присваивается ключ первого элемента, а переменной $value – его значение. При следующей итерации в переменные $kеу и $value запишутся соответственно ключ и значение следующего элемента. И так далее, пока массив не будет пройден полностью.

Естественно, у вас может возникнуть вопрос о способе реализации этого цикла, так как механизм перемещения по массиву остается скрытым от программиста. Дело в том, что любой массив помимо ключей и значений имеет внутренний указатель (pointer) или курсор, с помощью которого можно узнать, какой элемент мы сейчас рассматриваем. Этим указателем, естественно, можно управлять, то есть переносить его от одного элемента к другому.

Для работы с указателем применяются функции list(), each(), count() и др.