_.shuffle = function (arr) {
let arrCloned = arr.slice();
// 함수에 입력된 인자값을 수정하지 않고 arrCloned 변수에 인자값으로 입력받은 배열을 복사한다.
for (let fromIdx = 0; fromIdx < arr.length; fromIdx++) {
const toIdx = Math.floor(Math.random() * arr.length);
// toIdx는 (0 이상 1미만의 난수)와 (배열의 길이)를 곱한 값에서 소숫점 이하를 버린 값.
let temp = arrCloned[fromIdx];
// temp는 입력 배열의 n번째 요소. 반복문의 반복 횟수가 늘어날 수록 n번째는 점진적 증가.
arrCloned[fromIdx] = arrCloned[toIdx];
// 입력 배열의 n번째 요소는 입력 배열의 i번째 요소
arrCloned[toIdx] = temp;
// 입력 배열의 i번째 요소는 입력 배열의 n번째 요소
}
return arrCloned;
};
순서를 섞을 때에는 순서를 이리저리 랜덤하게 섞어야 한다.
위 코드에서는 순서를 랜덤하게 배정하기 위해 Math.floor(Math.random() * arr.length)
를 사용했다.
Math.random()
은 0보다 크고 1보다 작은(0 < n < 1) 임의의 난수를 생성하는 js 내장 메소드이다. (반복문이 반복할때마다 난수를 새로 생성한다)
이렇게 생성한 난수에 배열의 길이를 곱해주어야 하는데,
배열의 index를 뒤섞어 주어야 하기 때문에 배열의 길이를 곱하여 배열의 길이만큼만 분리할 수 있도록 한다.
난수에 배열의 길이를 곱하여 생긴 숫자는, 인덱스 지정을 할 수 있도록 Math.floor()
를 이용하여 소숫점 이하를 버린다.
순서 할당의 key point
배열의 길이를 곱해주어 임의의 index가 배열의 길이만큼만 생성되도록 한다Math.floor(Math.random() * arr.length) arr.length가 3일 경우 : 1~2 생성 arr.length가 5일 경우 : 1~4 생성 arr.length가 7일 경우 : 1~6 생성 arr.length가 9일 경우 : 1~8 생성
이와 같이
arr.length -1
의 임의의 값을 생성하기 때문에 셔플에 쓰기 좋다.
반복문이 돌면서 toIdx
에서 생성한 랜덤 인덱스를 이용하여 배열의 순서를 섞을 차례이다.
입력 받은 배열을 0번째 인덱스부터 마지막까지 순차적으로 순서를 바꾸기 위해
let temp = arrCloned[fromIdx]
위와 같이 temp라는 변수에 배열의 fromIdx번째 인덱스를 할당한다.
fromIdx는 반복문의 조건에서도 볼 수 있듯, 0부터 arr.length -1
까지 점진적으로 증가한다.
그리고 arrCloned[fromIdx] = arrCloned[toIdx]
를 이용하여 위치를 재할당 하고, toIdx
번째 위치에 있던 요소를 fromIdx
번째 위치로 재할당하여 옮긴다.
뒤섞기의 key point
얼핏 보면 2번의 재할당을 왜 하는지 이해하기 어려울 수 있지만, 작동하는 로직을 이해하면 쉽다.let temp = arrCloned[fromIdx] arrCloned[fromIdx] = arrCloned[toIdx] arrCloned[toIdx] = temp
이 코드에서 하는 역할은 간단하다.
fromIdx
번째 요소와toIdx
번째 요소를 맞바꾸는 것이다.fromIdx가 1이고 toIdx가 3일때, let temp = arrCloned[1] arrCloned[1] = arrCloned[3] arrCloned[3] = arrCloned[1]
이런 식으로 간단하게 각 인덱스의 요소를 맞바꾸어 주는 것 뿐이다.
코드는 어렵게 생각할 것 없이 로직만 잘 생각하면 술술 써내려 갈 수 있다.