БлогNot. Типовые алгоритмы с массивами на JavaScript: решаем задачи одной строчкой кода

Типовые алгоритмы с массивами на JavaScript: решаем задачи одной строчкой кода

Часто мы по инерции пишем типовые алгоритмы на JavaScript как на классическом Си или Паскале, то есть, с "ручной" организацией циклов и проверкой свойств.

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

Уже есть в блоге по теме: сортировка числового массива на JavaScript, функция array_unique (оставить только уникальные элементы) на JavaScript, матрица на JavaScript.

Вывод всех примеров делается просто в консоль браузера.

Узнать, является ли объект массивом
<script>
 let arr = [ 1, 2, 3 ];
 let val = 0;
 let obj = { index: 0, value: "val" };
 console.log(Array.isArray(arr)); // true
 console.log(Array.isArray(val)); // false
 console.log(Array.isArray(obj)); // false
</script>

Заполнить массив повторяющимися значениями
<script>
 let arr = Array(5).fill(0);
 console.log(arr); // [ 0, 0, 0, 0, 0 ]
</script>

Если нужно заполнение значениями 1,2,...,N, тогда так:

<script>
 const n = 5;
 let arr = Array(n).fill(0).map(function(val,index){return index+1;});
 console.log(arr); // [ 1, 2, 3, 4, 5 ]
</script>

Если массив двумерный, то есть, матрица, потребуется код вида

 let arr = Array(2).fill(0).map(() => {return Array(3).fill(0)});
 console.log (arr); //матрица 2x3 из нулей

а не просто

let arr = Array(2).fill(Array(3).fill(0));

для последующего правильного присваивания значений элементам этой матрицы.

Задать массив n случайных целых чисел из диапазона [n1;n2]
<script>
 const n = 10, n1 = -5, n2 = 5;
 let arr = [];
 arr = Array(n).fill(0).map(function(){return Math.floor(n1+Math.random()*(n2-n1+1));});
 console.log(arr);
</script>

Скопировать массив в другой массив
<script>
 let arr = [1,2,3,4,5];
 let arr2 = arr.slice();
 console.log(arr2); //[ 1, 2, 3, 4, 5 ]
</script>

Скопировать часть элементов массива на новые позиции (затирая предыдущие элементы)
<script>
 let arr = [1,2,3,4,5];
 let target=3,start=0,end=2;
 arr.copyWithin(target,start,end);
 console.log(arr); // [ 1, 2, 3, 1, 2 ]
</script>

Найти сумму элементов массива
<script>
 let arr = [1,2,3,4];
 let sum = arr.reduce((a, b) => a + b, 0);
 console.log(sum); //10
</script>

Вычислить какую-либо другую характеристику массива
<script>
 let arr = [ 1, 2, 3, 0, 5 ];
 let prod = arr.reduce((prod, val) => (val == 0 ? prod : prod*val), 1);
 console.log("Произведение ненулеых элементов = "+prod); 
  // Произведение ненулеых элементов = 30
</script>

Функция reduce применяется по очереди ко всем элементам массива и "переносит" свой результат на следующий вызов.

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

<script>
 let arr = [ 1, 2, 3, 0, 5 ];
 let prod = arr.reduce( 
  function(previousValue, item, index) { 
   return (item == 0 ? previousValue : previousValue*item); 
  }, 1 
 );
 console.log(prod); // 30
</script>

Метод reduceRight() аналогично применяет функцию к аккумулятору и каждому значению массива, сканируя последний справа налево.

Найти максимум или минимум в массиве

Можно как выше, а можно и по-другому.

<script>
 let arr = [2,4,3,1];
 let max = Math.max.apply(null, arr);
 let min = Math.min.apply(null, arr);
 console.log(min+' '+max); //1 4
</script>

Не рекомендуется применять этот подход для больших размерностей.

Узнать, если ли в массиве повторяющиеся элементы
<script>
 let arr1 = [2,4,3,1];
 let dup1 = (new Set(arr1)).size !== arr1.length;
 console.log(dup1); //false
 let arr2 = ['s1','s2','s1','s4'];
 let dup2 = (new Set(arr2)).size !== arr2.length;
 console.log(dup2); //true
</script>

Узнать, есть ли в массиве некоторое значение
<script>
 let arr = [2,4,3,1,2,1];
 let c = arr.includes(5);
 console.log(c); //false
</script>

Если ищем массив в массиве массивов, тогда так:

<script>
 function includesArray (arr, val) { //Есть ли массив val в массиве массивов arr
  //просто includes для массива массивов не подойдет
  arr = JSON.stringify(arr);
  val = JSON.stringify(val);
  return arr.indexOf(val) != -1;
 }
 let arr = [ [1,2], [3,4], [5,6] ];
 let val = [5,6];
 console.log (includesArray(arr, val)); //true
 console.log (includesArray(arr, [6,5])); //false
</script>

Найти индекс первого вхождения значения в массив
<script>
 let arr = [ 1, 2, 3, 4, 2 ];
 let cnt = 0;
 let n = arr.indexOf (2);
 console.log(n); // 1
</script>

Найти индекс последнего вхождения значения в массив
<script>
 let arr = [ 1, 2, 3, 4, 2 ];
 let cnt = 0;
 let n = arr.lastIndexOf (2);
 console.log(n); // 4
</script>

Если ничего не найдено, этот и предыдущий методы вернут -1.

Подсчитать количество вхождений значения в массив
<script>
 let arr = [2,4,3,1,2,1];
 let val = 1;
 let n = arr.filter(function(x){return x==val}).length;
 console.log(n); //2
</script>

Удалить из массива элемент или несколько элементов, начиная с нужного индекса
<script>
 let arr = [1,2,3,4,5];
 arr.splice(1,2); //индекс, количество
 console.log(arr); // [ 1, 4, 5 ]
</script>

А если нужно заменить удаляемые элементы другими?
<script>
 let arr = [1,2,3,4,5];
 arr.splice(1,2,10,10);
 console.log(arr); // [ 1, 10, 10, 4, 5 ]
</script>

Вставить в массив элементы, начиная с индекса n
<script>
 let arr = [1,2,3,4,5];
 let n = 2;
 arr.splice(n,0,10,11);
 console.log(arr); // [ 1, 2, 10, 11, 3, 4, 5 ]
</script>

Вырезать из массива подмассив элементов с индексами от n1 до n2
<script>
 let arr = [1,2,3,4,5];
 let n1 = 1, n2 = 3;
 let arr2 = arr.slice(n1,n2);
 console.log(arr2); // [ 2, 3 ]
</script>

Соединить массивы или массивы и дополнительные элементы
<script>
 let arr1 = [1,2];
 let arr2 = [4,5];
 let arr = [];
 arr = arr.concat(arr1,3,arr2);
 console.log(arr); // [ 1, 2, 3, 4, 5 ]
</script>

Преобразовать массив в строку без разделителя между элементами
<script>
 let arr = [1,2,3,4,5];
 let s = arr.join('');
 console.log(s); // 12345
</script>

Преобразовать массив в строку с элементами, разделёнными нужным разделителем
<script>
 let arr = [1,2,3,4,5];
 let s = arr.join(';');
 console.log(s); // 1;2;3;4;5
</script>

Преобразовать строку в массив элементов, разбивая её по нужному символу
<script>
 let s = '1;2;3;4;5;;'
 let arr = s.split(';');
 console.log(arr); // [ "1", "2", "3", "4", "5", "", "" ]
</script>

Здесь получится массив строк, а если нужен именно массив чисел из исходной строки, то

<script>
 let str = "0.07,-0.15,0.28,0.26,0.24,0";
 let arr = str.split(',').map(Number);
 console.log (arr); // массив чисел
</script>

Выполнить некую функцию для каждого элемента массива
<script>
 let arr = [ 1, 2, 3, 4, 5 ];
 let cnt = 0;
 arr.forEach(function(item, index) {
  if (item%2) cnt++;
 });
 console.log("Количество нечётных элементов в массиве = "+cnt);
 //Количество нечётных элементов в массиве = 3
</script>

Выполнить некую функцию для каждого элемента массива и вернуть массив результатов выполнения этой функции
<script>
 let arr = [ 1, -2, -3, 4, 5 ];
 let arr2 = arr.map(function(item, index) {
  return item<0 ? 0 : item;
 });
 console.log(arr2); // [ 1, 0, 0, 4, 5 ]
</script>

То есть, можно, например, сделать замену элементов по какому-то правилу.

Выполнить некую функцию для каждого элемента массива и проверить, есть ли хотя бы один истинный результат вызова функции
<script>
 let arr = [ 1, 2, 3, 4, 5 ];
 let arr2 = arr.some(function(item,index){return item>10;});
 console.log(arr2); // false
</script>

Выполнить некую функцию для каждого элемента массива и проверить, истинны ли все результаты вызова функции
<script>
 let arr = [ 1, 2, 3, 4, 5 ];
 let res = arr.every(function(item,index){return item<10;});
 console.log(res); // true
</script>

Найти в массиве объектов первый объект, отвечающий условию
<script>
 let users = [
  {id: 1, name: "Вася"},
  {id: 2, name: "Петя"},
  {id: 3, name: "Маша"}
 ];
 let user = users.find(item => item.id == 1);
 console.log(user); // Object { id: 1, name: "Вася" }
</script>

Если ничего не найдено, результат будет undefined

Найти в массиве объектов индекс первого объекта, отвечающего условию
<script>
 let users = [
  {id: 1, name: "Вася"},
  {id: 2, name: "Петя"},
  {id: 3, name: "Маша"}
 ];
 let user = users.findIndex(item => item.id == 2);
 console.log(user); // 1
</script>

Если ничего не найдено, результат будет -1

Перевернуть массив
<script>
 let arr = [ 1, 2, 3, 4, 5 ];
 arr.reverse();
 console.log(arr); // [ 5, 4, 3, 2, 1 ]
</script>

Отфильтровать массив (оставить элементы, отвечающие нужному условию или условиям)

Удобнее всего - стандартный метод filter со стрелочной функцией обратного вызова в качестве аргумента. Аргумент x самой функции означает очередной элемент массива. Если функция вернёт true, элемент исходного массива добавляется в преобразованный массив, который и будет результатом вызова функции filter.

<script>
 let arr = [5, 4, 3, 8, 0];
 let a = 3, b = 5;
 let filtered = arr.filter (x => x>=a && x<=b);
  //элементы из интервала [3, 5]
 console.log (filtered); // [5, 4, 3]
</script>
<script>
 let arr = [1, 0, -2, -1, 3];
 let filtered = arr.filter (x => x>0);
  //положительные элементы
 console.log (filtered); // [1, 3]
</script>

Итого и другие простые методы массивов

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

Для добавления/удаления элементов:

  • unshift(...items) – добавляет элементы в начало;
  • shift() – извлекает элемент из начала;
  • push (...items) – добавляет элементы в конец;
  • pop() – извлекает элемент с конца.
  • splice(pos, deleteCount, ...items) – начиная с индекса pos, удаляет deleteCount элементов и вставляет items;
  • slice(start, end) – создаёт новый массив, копируя в него элементы с позиции start до end (не включая end);
  • fill(value, start, end) - позволяет заполнить массив нужным значением;
  • copyWithin(target,start,end) - переписывает часть элементов внутри массива;
  • concat(...items) – возвращает новый массив: копирует все члены текущего массива и добавляет к нему items. Если какой-то из items является массивом, тогда берутся его элементы.

Для поиска среди элементов:

  • indexOf / lastIndexOf(item, pos) – ищет item, начиная с позиции pos, и возвращает его индекс или -1, если ничего не найдено;
  • includes(value) – возвращает true, если в массиве имеется элемент value, в противном случае false;
  • find(func) / filter(func) – фильтрует элементы через функцию и отдаёт первое/все значения, при прохождении которых через функцию возвращается true;
  • findIndex(func) похож на find, но возвращает индекс вместо значения;
  • some(func) / every(func) - проверяют, выполняется ли некоторое свойство хотя бы для одного / для всех элементов массива.

Для перебора элементов:

  • Array.isArray(arr) - проверяет, является ли arr массивом;
  • forEach(func) – вызывает func для каждого элемента. Ничего не возвращает.

Для преобразования массива:

  • map(func) – создаёт новый массив из результатов вызова func для каждого элемента;
  • sort(func) – сортирует массив "на месте", а потом возвращает его;
  • reverse() – "на месте" меняет порядок следования элементов на противоположный и возвращает изменённый массив;
  • split(div) / join(div) – преобразуют строку в массив и обратно, применяя разделитель div;
  • reduce(func, initial) – вычисляет одно значение на основе всего массива, вызывая func для каждого элемента и передавая промежуточный результат между вызовами.

Обратите внимание, что ряд методов (sort, reverse, splice, copyWithin) изменяют исходный массив.

Этих методов обычно достаточно для удобной работы с массивами.

 Всё о массивах в JavaScript от Mozilla

22.02.2020, 11:05 [10220 просмотров]


теги: программирование javascript список алгоритм

показать комментарии (1)