JavaScript | Как перемешать массив?

JavaScript | Как перемешать массив?

 

Самый простой способ перемешивания массива в JavaScript

let arr = [1,2,3,4,5,6,7,8,9,10,11]

arr.sort(()=>Math.random()-0.5)

[4, 1, 9, 6, 2, 10, 5, 11, 3, 8, 7]

 

Самый простой способ перемешать массив в JavaScript
Самый простой способ перемешать массив в JavaScript

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

Начнём с метода sort() т. к. вся «магия» происходит внутри него. Из стандарта ECMAScript мы знаем, что метод sort() принимает в качестве первого (и единственного) параметра функцию сравнения. Она может быть определена, а может и не быть определена.

 

Если функция сравнения (comparefn) ОПРЕДЕЛЕНА, то она принимает два аргумента x и y и возвращает:

  • отрицательное Число (Number), если x < y,
  • положительное Число (Number), если x > y,
  • ноль в противном случае.

И вот тут есть нюанс! В стандарте не сделан акцент на тот случай, когда мы не передаём параметры в функцию сравнения (comparefn). Нам просто сказали, что функция сравнения (comparefn) должна возвращать какие-то числа. И всё.

Что нам мешает воспользоваться такой конструкцией, которая будет выдавать либо отрицательное, либо положительное, либо ноль? Ничего! В JavaScript как раз существует такая конструкция — это Math.random()-0.5.

Почему «-0.5»? Ответ. Только в таком выражении мы можем получать случайные положительные или отрицательные числа в пределах длины 1. Мы знаем что Math.random() даёт нам только положительные числа в диапазоне между 0 и 1, но не включая один. Если так всё и оставить, то мы всегда будем получать только положительные числа — в результате не будет происходить перестановка элементов массива так как знаки не чередуются. Чтобы массив перемешивался, знаки чисел должны как-то чередоваться. Именно поэтому нужно вычитать ровно половину (то есть 0.5) для сбалансированного попадания положительных в отрицательные. Например:

Если выпадет 0.7, то получим 0.2, если 0.4, то -0.1.

 

Как случайным образом поменять местами элементы массива? (Array shuffle)

Возьмём простой массив для тестов:

var massiv = [100, 101, 102, 103, 104, 105, 106, 107]

Из этого массива мы хотим получить другой массив, чтобы его элементы были перемешаны относительно оригинального массива. Как это сделать?

Нам поможет конструктор Math и его метод метод random().

 

Видео

 

Функция перемешивания массива

function mixarr(arr){
   return arr.map(i=>[Math.random(), i]).sort().map(i=>i[1])
}

Запускаем тестирование № 1:

mixarr(massiv)
Перемешали массив JavaScript - вызов 1
Перемешали массив JavaScript — вызов 1

Запускаем тестирование № 2:

mixarr(massiv)
Перемешали массив JavaScript - вызов 2
Перемешали массив JavaScript — вызов 2

 

Запускаем тестирование № 3:

mixarr(massiv)
Перемешали массив JavaScript - вызов 3
Перемешали массив JavaScript — вызов 3

Метод перемешивания массива

Мы можем расширить стандартный набор методов массивов для класса Array.

Array.prototype.mixarr = function (){
   return this.map(i=>[Math.random(), i]).sort().map(i=>i[1])
}

Пример работы:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].mixarr()
[4, 10, 9, 7, 1, 5, 2, 6, 8, 3]
Свой метод перемешивания массива - JavaScript
Свой метод перемешивания массива — JavaScript

 

Как работает функция и метод? Логика работы

Мы проходим по каждому элементу массива методом map(). На каждом шаге метода мы возвращаем пары вида:

[ случайное число от 0 до 1 (но не 1) , элемент массива (как есть) ]

Таких пар будет ровно столько, сколько элементов в оригинальном массиве.

Например:

Создали пары из случайного числа и элемента массива - JavaScript
Создали пары из случайного числа и элемента массива — JavaScript

Когда мы получаем массив из массивов, то теперь можем провести сортировку методом sort(). По-умолчанию он будет сортировать по первому элементу вложенных массивов, которые являются случайными числами. Порядок сортировки не имеет значения т. к. любая сортировка автоматически перемешает пары, а значит второй элемент в паре уже будет не на своём первоначальном месте.

Отсортировали пары по первому элементу - JavaScript
Отсортировали пары по первому элементу — JavaScript

После перемешивания пар нам останется только удалить первые элементы внутренних массивов, которые по сути не имеют информационного смысла и нужны только для работы алгоритма. «Удаляем» — это возвращаем только вторые элементы внутренних массивов.

 

Итог

Элементы массива перемешиваются. Функция меняет местами элементы массива и возвращает новый массив без повреждения оригинального.

 

Информационные ссылки

JavaScript | Массивы (Array)

JavaScript | Случайный элемент массива

Стандарт ECMAScript — Раздел «23.1.3.28 Array.prototype.sort ( comparefn )» — https://tc39.es/ecma262/#sec-array.prototype.sort

Стандарт ECMAScript — Раздел «21.3.2.27 Math.random ( )» — https://tc39.es/ecma262/#sec-math.random